BZOJ 4175: 小G的电话本 SAM+FFT

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

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;
}

  

 
posted @ 2017-03-20 08:07  Oncle_Ha  阅读(261)  评论(0编辑  收藏  举报