这道题容斥想了半天。。。
拉丁字母共 52个 2^52 = 4e15, 不会爆long long,于是先给字母编号
inline int calc(char c) { if (c < 'a') return c - 'A'; return (c - 'a' + 26); }
这样对于当前状态 ll res, res的二进制第i位(以下进制都是二进制)为1表示标号为(i-1)的字母出现了奇数次
这样我们就可以
1.把状态相等的 res,归为一类,这一类对答案的贡献就是 size * (size - 1) / 2
2.然而不光是同类相减出现回文串,只要两个状态相差一个字母的奇偶不同相减就可以产生一个答案,所以我们还要让每个集合与相邻的集合去对答案做贡献
3.对于不靠状态相减就能对答案做贡献的只有 res = 0 或 1
对于单个字母,都是靠相减剪出来的(比如第i(i != 1)个字母,是靠res(i) - res(i - 1)剪出来对答案做贡献的 条件2)(对于第一个字母,是由 条件3对答案做贡献的)
具体实现见代码
#include<bits/stdc++.h> #define ll long long using namespace std; const int maxn = 2e5 + 5; int n, f[maxn], siz[maxn], cnt[52]; char s[maxn]; map<ll, int> mp; vector<ll> ve[49]; inline int calc(char c) { if (c < 'a') return c - 'A'; return (c - 'a' + 26); } void unit(int x, int y) { f[y] = x; ++siz[x]; } int main() { scanf("%d%s", &n, s + 1); int flag = 0; ll res = 0, ans = 0; for (int i = 1; s[i]; ++i) { f[i] = i, siz[i] = 1; int id = calc(s[i]); if (++cnt[id] % 2) ++flag; else --flag; if (flag <= 1) ++ans; //条件3 res ^= 1ll << id; if (!mp.count(res)) mp[res] = i, ve[flag].emplace_back(res); else unit(mp[res], i); } if (!ve[0].empty()) { int id = mp[ve[0][0]]; ans += (ll)siz[id] * (siz[id] - 1) >> 1; //条件1 } for (int i = 48; i; --i) { for (int j = 0; j < ve[i].size(); ++j) { res = ve[i][j]; int id = mp[res]; ans += (ll)siz[id] * (siz[id] - 1) >> 1; //条件1 for (int k = 0; k < 52; ++k) if (res & (1ll << k)) { ll r = res ^ (1ll << k); if (mp.count(r) == 0) continue; ans += (ll)siz[id] * siz[mp[r]]; //条件2 } } } printf("%lld", ans); return 0; }