问题描述
。。。我不太想说 NOIP2020噩梦
问题解决
君子报仇,十年不晚
这道题考场上写了一个(O(nlognlog26))的解法,不够优秀被卡掉了
后面看了题解,然后写了一个(O(nlog26))的解法
引入拓展(kmp)中的(z)函数:(z[i])表示(s[i,n-1])与(s)本身的最长公共前缀的长度
先不考虑奇数,对于循环节为(i)的字符串,可以计算上的就是(leftlfloordfrac{z[i]}{i} ight floor +1)
然后对于 (k) 分奇偶讨论,我们发现奇数和偶数满足的条件是相同的,于是乘法原理加上树状数组统计一下就好了
代码实现
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=(1<<20)+5;
char s[maxn];
int n,z[maxn],before[30],after[30],pre,suf,all;
int c[30],T;
inline void add_x(int x,int data){
for(int i=x;i<=27;i+=i&-i)c[i]+=data;
}
inline int get(int x){
int ret=0;
for(int i=x;i;i-=i&-i)ret+=c[i];
return ret;
}
void Z(){
z[0]=n; int now=0;
while(now+1<n&&s[now]==s[now+1])now++;
z[1]=now;int p0=1;
for(int i=2;i<n;i++){
if(i+z[i-p0]<p0+z[p0]) z[i]=z[i-p0];
else {
now=p0+z[p0]-i;now=max(now,0);
while(now+i<n&&s[now]==s[now+i])now++;
z[i]=now;p0=i;
}
}
}
int main(){
freopen("P7114.in","r",stdin);
freopen("P7114.out","w",stdout);
scanf("%d",&T);
while(T--){
scanf("%s",s);
n=strlen(s);
memset(before,0,sizeof before);
memset(after,0,sizeof after);
memset(c,0,sizeof c);
all=pre=suf=0;Z();
for(int i=0;i<n;i++) i+z[i]==n&&(z[i]--,0);
for(int i=0;i<n;i++) after[s[i]-'a']++;
for(int i=0;i<26;i++) (after[i]&1)&&(all++,0);
suf=all;LL ans=0;
for(int i=0;i<n;i++){
if(after[s[i]-'a']&1)suf--;else suf++;after[s[i]-'a']--;
if(before[s[i]-'a']&1)pre--;else pre++;before[s[i]-'a']++;
if(i!=0&&i!=n-1){
int t=z[i+1]/(i+1)+1;
ans+=1LL*(t/2)*get(all+1)+1LL*(t-t/2)*get(suf+1);
}
add_x(pre+1,1);
}
printf("%lld
",ans);
}
}