C. Poman Numbers
题意
给定一个字符串和一个函数 f ( S ) f(S) f(S)。
f ( S ) = { 2 p o s ( S [ 1 ] ) ∣ S ∣ = 1 − f ( S ( 1 , m ) ) + f ( S ( m + 1 , ∣ S ∣ ) ) ∣ S ∣ > 1 f(S)=\begin{cases} 2^{pos(S[1])} & |S|=1\\ -f(S(1,m))+f(S(m+1,|S|)) &|S|>1 \end{cases} f(S)={2pos(S[1])−f(S(1,m))+f(S(m+1,∣S∣))∣S∣=1∣S∣>1
其中
p
o
s
(
c
)
pos(c)
pos(c) 表示字母
c
c
c 相对 a
的位置。
解法
-
题目可以看作字符串的每个字母都代表着一个值: 2 p o s ( S [ i ] ) 2^{pos(S[i])} 2pos(S[i]) ,然后所有值对字符串的值做正贡献或负贡献;
-
显然最后一个字母做正贡献,倒数第二个字母做负贡献;
-
接下来证明前 n − 2 n-2 n−2 字母贡献可正可负;
-
假设最终希望的贡献是 − − − + + − + − + ---++-+-+ −−−++−+−+ ,那么前面的负贡献可以每次令 m = 1 m=1 m=1 得到,剩下的是 + + − + − + ++-+-+ ++−+−+ ,可以令 m = n − 2 m=n-2 m=n−2 ,即 ( − − + − ) − + (--+-)-+ (−−+−)−+ ,只需要得到 − − + − --+- −−+−即可,那么问题就可以这样递归解决了。
-
前 n − 2 n-2 n−2 项贡献可正可负,贪心即可。
代码
#pragma region
//#pragma optimize("Ofast")
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <vector>
using namespace std;
typedef long long ll;
#define tr t[root]
#define lson t[root << 1]
#define rson t[root << 1 | 1]
#define rep(i, a, n) for (int i = a; i <= n; ++i)
#define per(i, a, n) for (int i = n; i >= a; --i)
#pragma endregion
const int maxn = 1e5 + 5;
char s[maxn];
int main() {
ll n, m;
scanf("%lld%lld", &n, &m);
scanf("%s", s + 1);
m -= (1LL << (s[n] - 'a')) - (1LL << (s[n - 1] - 'a'));
vector<int> cnt(26);
rep(i, 1, n - 2) cnt[s[i] - 'a']++;
for (int i = 25; i >= 0; --i) {
while (cnt[i]) {
ll x = m - (1LL << i);
ll y = m + (1LL << i);
m = abs(x) < abs(y) ? x : y;
cnt[i]--;
}
}
puts(m ? "No" : "Yes");
}