4175: 小G的电话本
Time Limit: 45 Sec Memory Limit: 256 MB
Submit: 195 Solved: 48
[Submit][Status][Discuss]
Description
小G是一个商人,他有一个电话本。电话本上记下了许多联系人,如timesqr、orzyhb等等。不过Tony对其中的某个联系人的名字S特别感兴趣,他从中提取出了这个联系人的名字中的所有片段,如提取出orz的 o、r、z、or、rz、orz等等。现在他想请你统计有多少个长度为k的片段对(P[1], P[2], P[3], ..., P[k]),使得在该片段对中所有片段在S中出现次数之和为他的幸运数m?注意两个片段对不同当且仅当两个片段对的某一位的片段不同,两个片段不同当且仅当这两个片段在S中的位置不同
Input
第一行两个整数k和m,意义见题目描述;第二行给出一个字符串表示Tony喜欢的联系人名字S。
Output
输出一行一个整数ans,表示答案模1005060097。
Sample Input
3 4
aaaaa
aaaaa
Sample Output
6
HINT
【样例解释】
符合要求的片段对一共有6种(用[p]s表示起始位置为p的s片段):
([1]aaaa, [1]aaaaa, [1]aaaaa)、([1]aaaaa, [1]aaaa, [1]aaaaa)、
([1]aaaaa, [1]aaaaa, [1]aaaa)、([2]aaaa, [1]aaaaa, [1]aaaaa)、
([1]aaaaa, [2]aaaa, [1]aaaaa)、([1]aaaaa, [1]aaaaa, [2]aaaa)。
【数据范围】
设n表示联系人的名字的长度,联系人的名字只包含小写字母。
对于10%的数据,1 <= n <= 100, k = 1。
对于40%的数据,1 <= n <= 100, k = 2。
对于70%的数据,1 <= n <= 100000, 1 <= k <= 10。
对于100%的数据,1 <= n <= 100000, 1 <= k <= 100000, 1 <= m <= n。
题意:
选出k个可以相同的子串,使他们在S中出现次数之和为m。求方案数。(看样例读题系列。
想法:
弱化版:在数集{S}中选k个可以重复的数字,使他们之和为m,求方案数。
设b[i]表示数值为i的个数,C[i]为其之和为i的方案数。
当k=1时,C=b,输出C[m]
当k=2时,C[i]=∑b[j]*b[i-j]
当k=3时,C[i]=∑b[j]*b[i-j],C[i]=∑C[j]*b[i-j]
......
当k=x时,C[i]=∑b[j]*b[i-j],C[i]=∑C[j]*b[i-j]...C[i]=∑C[j]*b[i-j] 共x-1次卷积
所以C=b^k,FFF+快速幂O(nlog^2n) ps:有更快的方法
回到原题,b[i]表示出现i次的子串个数,建出SAM后遍历每个节点:
b[|right(i)|]+=(sam[i].max-sam[i].min+1)*|right(i)|
总O(nlog^2n)
#include<cstdio>
#include<cstring>
#define ll long long
const int len(100000),MP(1005060097),g(5),lem(262144);
struct SamNode{int nx[26],pre,right,step;}sam[len*2+10];
int top=1,root=1,now=1,last,lastson;
int cnt[len+10],p[len*2+10],b[len+10];
int n,m,k;char str[len+10];
void insert(int x)
{
last=now;now=++top;sam[now].step=sam[last].step+1;sam[now].right=1;
for(;!sam[last].nx[x]&&last;last=sam[last].pre)
sam[last].nx[x]=now;
if(!last)sam[now].pre=root;
else
{
lastson=sam[last].nx[x];
if(sam[lastson].step==sam[last].step+1)sam[now].pre=lastson;
else
{
++top;
memcpy(sam[top].nx,sam[lastson].nx,sizeof(sam[top].nx));
sam[top].pre=sam[lastson].pre;
sam[top].step=sam[last].step+1;
sam[lastson].pre=sam[now].pre=top;
for(;sam[last].nx[x]==lastson&&last;last=sam[last].pre)
sam[last].nx[x]=top;
}
}
}
void update()
{
for(int i=1;i<=top;i++)cnt[sam[i].step]++;
for(int i=1;i<=n;i++)cnt[i]+=cnt[i-1];
for(int i=top;i>=1;i--)p[cnt[sam[i].step]--]=i;
for(int i=top;i>=1;i--)
{
sam[sam[p[i]].pre].right+=sam[p[i]].right;
b[sam[p[i]].right]=(b[sam[p[i]].right]+1ll*sam[p[i]].right*(sam[p[i]].step-sam[sam[p[i]].pre].step))%MP;
}
} int R[lem+10],w[lem+10],wn,il,l,h;
void swap(int &a,int &b){if(a==b)return;a^=b;b^=a;a^=b;}
int power(int a,int b)
{
if(b<0)b=MP-1+b; long long t=1,y=a;
while(b){if(b&1)t=(t*y)%MP;y=(y*y)%MP;b>>=1;}
return (int)t;
}
struct Arr
{
void NTT(int *a,int l,int ty)
{
for(int i=0;i<l;i++)
if(i<R[i])swap(a[i],a[R[i]]);
for(int leng=2;leng<=l;leng<<=1)
{
int M=leng>>1;
wn=power(g,ty*((MP-1)/leng));
for(int i=1;i<M;i++)w[i]=(1ll*w[i-1]*wn)%MP;
for(int i=0;i<l;i+=leng)
{
for(int j=0;j<M;j++)
{
int x=a[i+j],y=(1ll*w[j]*a[i+j+M])%MP;
a[i+j]=x+y;a[i+j+M]=x-y;
a[i+j]-=a[i+j]>=MP?MP:0;
a[i+j+M]+=a[i+j+M]<0?MP:0;
}
}
}
if(ty==-1)
for(int i=0;i<l;i++)a[i]=(1ll*a[i]*il)%MP;
}
int a[lem+10];//只保留前m项
void mul(Arr &C)
{
NTT(a,l,1);
NTT(C.a,l,1);
for(int j=0;j<l;j++)a[j]=(1ll*a[j]*C.a[j])%MP;
NTT(a,l,-1);for(int j=m+1;j<l;j++)a[j]=0;
}
}X,Y,C;
void deal()
{
l=1;while(l<=m+m+1)l<<=1,h++;
for(int i=0;i<l;i++)R[i]=(R[i>>1]>>1|(i&1)<<(h-1));
il=power(l,MP-2);
}
int main()
{
// freopen("C.in","r",stdin);
// freopen("C.out","w",stdout);
scanf("%d%d",&k,&m);
scanf("%s",str);n=strlen(str);
for(int i=0;i<n;i++)insert(str[i]-'a');
update();deal();w[0]=1;
X.a[0]=1;for(int i=0;i<=m;i++)Y.a[i]=b[i];//,printf("%d\n",b[i])
for(;k;k>>=1,C=Y,Y.mul(C))
{
// printf("Y\n");
// for(int i=0;i<=m;i++)printf("%d\n",Y.a[i]);
if(k&1)C=Y,X.mul(C);
// printf("X\n");
// for(int i=0;i<=m;i++)printf("%d\n",Y.a[i]);
}
printf("%d",X.a[m]);
return 0;
}