[arc077f]SS

来源:互联网 发布:exe打包软件 编辑:程序博客网 时间:2024/05/22 15:57

前言

有趣的字符串题。
我是不是推出了什么和大家看起来不一样实质上一样的东西。

题目大意

定义AA表示两个一样的字符串A接在一起。
定义F(SS)=TT,TT是长度最小的满足条件的字符串且SS是TT的前缀。
给你小写字母串SS,求F10100(SS)的区间[l,r]每个字母出现次数。

有趣

这显然是一道和border相关的题。
然后可以发现只考虑S的变化就行,不需要考虑SS。
先做一遍kmp求出nxt(S)。
我们不妨分类讨论一下。
如果S本身就是一个循环串,容易发现F了10^100次后就是这个循环节无限循环,只需要统计一个循环节内每个字母出现次数即可,最后剩余一点再去统计对应前缀。
如果不是循环串呢?
有一个结论是它永远不会变成循环串。
如果nxt(S)>=(|S|+1)/2,那么我们推一下发现经过一次F变化后nxt反而变小(nxt会变成border长度,可以证明不会更长),然后就转化为nxt(S)<(|S|+1)/2的情况。
那么不妨只讨论nxt(S)<(|S|+1)/2的情况,假如这时串是ABA。其中A是前nxt(S)个字符组成的字符串。
一次变换后会变成ABAAB,nxt变成AB(可以用反证法证明不会更长,否则原串是循环串),假如设cnt1表示A中某字母出现次数,cnt2表示B中某字母出现次数。
那么一次变换|S’|=2*|S|-nxt(S),nxt(S’)=|S|-nxt(S),cnt1’=cnt1+cnt2,cnt2’=cnt1。
于是可以注意到nxt(S)<(|S|+1)/2永远满足,然后可以发现一次变换|S|至少变为原来的1.5倍,因此爆1e18只需要log次。不妨暴力处理出来,就很好求答案了。
可以参考代码实现。

#include<cstdio>#include<algorithm>#include<cstring>#define fo(i,a,b) for(i=a;i<=b;i++)#define fd(i,a,b) for(i=a;i>=b;i--)using namespace std;typedef long long ll;const int maxn=200000+10;const ll inf=1000000000000000000;char s[maxn];ll len[1000],nt[1000],cnt1[1000][27],cnt2[1000][27],o[27],ans[27];int nxt[maxn];int i,j,k,t,n,m,tot,top;ll l,r;void kmp(){    j=0;    fo(i,2,n){        while (j&&s[j+1]!=s[i]) j=nxt[j];        if (s[j+1]==s[i]) j++;        nxt[i]=j;    }}void work(ll n,int f){    int i,j;    fd(i,top,1)        if (n>=len[i]){            fo(j,0,25) ans[j]+=f*(cnt1[i][j]*2+cnt2[i][j]);            n-=len[i];        }    if (n>=len[0]){        fo(j,0,25) ans[j]+=f*o[j];        n-=len[0];    }    fo(i,1,n) ans[s[i]-'a']+=f;}void solve(ll n,int f){    int i,j;    fo(i,0,25) ans[i]+=(ll)f*(n/tot)*o[i];    n%=tot;    fo(i,1,n) ans[s[i]-'a']+=f;}int main(){    scanf("%s",s+1);    n=strlen(s+1);    n/=2;    kmp();    if (n%(n-nxt[n])){        fo(i,1,nxt[n]) cnt1[0][s[i]-'a']++;        fo(i,nxt[n]+1,n-nxt[n]) cnt2[0][s[i]-'a']++;        fo(i,1,n) o[s[i]-'a']++;        len[0]=n;nt[0]=nxt[n];        if (nxt[n]>=(n+1)/2){            top++;            len[top]=len[top-1]*2-nt[top-1];            nt[top]=len[top-1]-nt[top-1];            fo(i,1,nt[top]) cnt1[top][s[i]-'a']++;            fo(i,n-nxt[n]+1,n) cnt2[top][s[i]-'a']++;        }        while (len[top]<inf){            top++;            len[top]=len[top-1]*2-nt[top-1];            nt[top]=len[top-1]-nt[top-1];            fo(i,0,25){                cnt1[top][i]=cnt1[top-1][i]+cnt2[top-1][i];                cnt2[top][i]=cnt1[top-1][i];            }        }        scanf("%lld%lld",&l,&r);        work(r,1);        work(l-1,-1);        fo(i,0,25) printf("%lld ",ans[i]);    }    else{        tot=n-nxt[n];        fo(i,1,tot) o[s[i]-'a']++;        scanf("%lld%lld",&l,&r);        solve(r,1);        solve(l-1,-1);        fo(i,0,25) printf("%lld ",ans[i]);    }}
原创粉丝点击