题面
(T) 组测试数据。给定字符串 (S),问有多少不同的非空字符串 (A),(B),(C) 满足 (S=ABABAB...ABC) 且 (A) 中出现奇数次的字符数不多于 (C)。
数据范围:(1le Tle 5),(1le |S|le 2^{20})。
这估计是我场上唯一做出来的题目了,NOIP2020 游记 也不放洛谷博客里了。
提供一个 (Theta(n)) 的做法,下标从 (0) 开始。
求出 (S) 的 Z
数组,(ze_i) 表示满足 (s[0,ze_i)=s[i,i+ze_i)) 的最大值。
根据 (s[0,ze_i)=s[i,i+ze_i)) 可以推出 (s[i,ze_i)=s[2i,i+ze_i))。
所以可以枚举 (AB) 的长度 (i),易得这个串在前缀重复了 (lfloorfrac{ze_i}{i} floor+1) 次。
然后解决关于出现奇数次的字符的限制。
维护一个树状数组,表示每个 (j<i) 的前缀出现奇数次字符数的集合。
计算前缀出现奇数次字符数可以 (Theta(1)),值域为字符集的树状数组时间复杂度也是 (Theta(1)) 的。
考虑对于当前的所有 (ABAB...AB) 对应的 (C) 中出现奇数次字符数:只有两种可能。
一种是当前后缀出现奇数次字符数,一种是全局出现奇数次字符数。先预处理后者,前者可以 (Theta(1)) 维护。
因为 (ABAB) 中出现奇数次字符数肯定为 (0),所以对于 (lfloorfrac {lfloorfrac{ze_i}{i} floor+1}{2} floor) 的肯定是全局次数,(lceilfrac {lfloorfrac{ze_i}{i} floor+1}{2} ceil) 的肯定是后缀次数。
然后对于两种情况,答案加上树状数组查询前缀满足条件的 (A) 的数量乘以出现次数即可。
所以总时间复杂度 (Theta(n))。
代码
回家手敲的。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define x first
#define y second
#define bg begin()
#define ed end()
#define pb push_back
#define mp make_pair
#define sz(a) int((a).size())
#define R(i,n) for(int i(0);i<(n);++i)
#define L(i,n) for(int i((n)-1);i>=0;--i)
const int iinf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f;
//Data
const int N=1<<20,C=26;
int n,all,last,now,tot[C],cnt[C];
string str;
//FenwickTree
int fen[C|1];
void add(int i,int v){for(;i<C+1;i|=i+1) fen[i]+=v;}
int sum(int i,int v=0){for(;i>=0;(i&=i+1)--) v+=fen[i]; return v;}
//Zebra
int ze[N];
void zebra(){
int l=0;
R(i,n) ze[i]=0;
R(i,n)if(i){
if(l+ze[l]>i) ze[i]=min(l+ze[l]-i,ze[i-l]);
while(i+ze[i]<n&&str[ze[i]]==str[i+ze[i]]) ze[i]++;
if(i+ze[i]>l+ze[l]) l=i;
}
}
//Main
void Main(){
cin>>str,n=sz(str),zebra();
R(i,n)if(i+ze[i]==n) ze[i]--;
ll ns=all=last=now=fen[C]=0;
R(c,C) tot[c]=cnt[c]=fen[c]=0;
R(i,n) tot[str[i]-'a']++;
R(c,C) all+=(tot[c]&1); last=all;
R(i,n){
if(tot[str[i]-'a']&1) last--; else last++;
if(cnt[str[i]-'a']&1) now--; else now++;
tot[str[i]-'a']--,cnt[str[i]-'a']++;
if(i&&i<n-1){
int t=ze[i+1]/(i+1)+1;
// cout<<"i="<<i<<" t="<<t<<'
';
ns+=1ll*(t/2)*sum(all)+1ll*(t-t/2)*sum(last);
}
// cout<<"now="<<now<<" last="<<last<<" all="<<all<<'
';
add(now,1);
}
cout<<ns<<'
';
}
int main(){
//freopen("string.in","r",stdin);
//freopen("string.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int T; cin>>T;
while(T--) Main();
return 0;
}
祝大家学习愉快!