Problem Palindrome


这道题容斥想了半天。。。
拉丁字母共 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;
}

浙公网安备 33010602011771号