【NOI】2017 蚯蚓排隊(BZOJ 4943,LOJ 2303) 模擬+hash

【題目】#2303. 「NOI2017」蚯蚓排隊 【題意】給定n條長度不超過6的蚯蚓,初始各自在一個隊伍。m次操做:1.將i號蚯蚓和j號蚯蚓的隊伍合併(保證i爲隊尾,j爲隊首)。2.將i號蚯蚓和它後面的蚯蚓分離成兩個隊。3.詢問:給定字符串S和正整數k,求f(每一個長度爲k的子串)的乘積。其中f(S)定義爲蚯蚓在其隊伍向後延伸k位組成的字符串等於S的蚯蚓個數。$n \leq 210^5,m \leq 510^5,k \leq 50,\sum |s| \leq 10^7,c \leq 10^3$,其中c爲操做2的數量。 【算法】模擬+hash 【參考】LZZの知乎回答 暴力一點考慮,每一個隊伍維護一條鏈,合併和分裂時維護$c_{x,i}$表示第x號蚯蚓向後延伸i位的字符串hash值並更新長度爲i的答案,查詢的時候直接枚舉子串訪問長度爲i的答案中hash值相同的。 容易知道這樣最壞複雜度是$O(mk^2+\sum |s|)$,理論上依然沒法經過。git

繼續分析。 考慮分離操做至多c次,複雜度爲$O(ck^2)$。 考慮合併操做,由於每次合併其實顯然是不須要枚舉滿的,是否有可能省去一個k的複雜度?合併的終串中每一個子串只會被統計一次(分離只有1000次,不影響複雜度),而總共有nk個子串,因此複雜度爲$O(nk)$。算法

總複雜度$O(ck^2+nk+\sum |s|)$。 注意:我用的hash方式是一個小哈希存鄰接表,一個大哈希當成真實值來檢驗。spa

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
bool isdigit(char c){return c>='0'&&c<='9';}
int read(){
	int s=0,t=1;char c;
	while(!isdigit(c=getchar()))if(c=='-')t=-1;
	do{s=s*10+c-'0';}while(isdigit(c=getchar()));
	return s*t;
}
using namespace std;
const int maxn=200010,MOD=998244353,cmod=3000007,dmod=1000000007,basec=233,based=123,maxk=60,maxL=10000010;
int first[cmod+5],tot,n,m,a[maxL],b[maxL],E[maxk],f[maxk],A[maxn],c[maxn][maxk],d[maxn][maxk],nxt[maxn],pre[maxn];
char s[maxL];
struct edge{int k,v,x,from;}e[maxn*maxk];
void insert(int k,int u,int v,int x){
	for(int i=first[u];i;i=e[i].from)if(e[i].k==k&&e[i].v==v){
		e[i].x+=x;
		return;
	}
	e[++tot]=(edge){k,v,x,first[u]};first[u]=tot;
}
int cM(int x){return x>=cmod?x-cmod:x;}
int dM(int x){return x>=dmod?x-dmod:x;}
int main(){
	n=read();m=read();
	E[0]=1;for(int i=1;i<=50;i++)E[i]=1ll*E[i-1]*basec%cmod;
	f[0]=1;for(int i=1;i<=50;i++)f[i]=1ll*f[i-1]*based%dmod;
	for(int i=1;i<=n;i++){
		A[i]=read();
		c[i][1]=d[i][1]=A[i];
		insert(1,A[i],A[i],1);//
	}
	while(m--){
		int kind=read();
		if(kind==1){
			int x=read(),y=read();
			nxt[x]=y;pre[y]=x;
			for(int i=1;i<50&&x;i++){
				int z=y;
				for(int j=1;i+j<=50&&z;j++){
					c[x][i+j]=(1ll*c[x][i+j-1]*basec+A[z])%cmod;
					d[x][i+j]=(1ll*d[x][i+j-1]*based+A[z])%dmod;
					insert(i+j,c[x][i+j],d[x][i+j],1);
					z=nxt[z];
				}
				x=pre[x];
			}
		}
		else if(kind==2){
			int x=read(),y=nxt[x];
			nxt[x]=pre[y]=0;
			for(int i=1;i<50&&x;i++){
				int z=y;
				for(int j=1;i+j<=50&&z;j++){
					insert(i+j,c[x][i+j],d[x][i+j],-1);
					z=nxt[z];
				}
				x=pre[x];
			}
		}
		else{
			scanf("%s",s+1);int len=strlen(s+1),k=read();
			for(int i=1;i<=len;i++)a[i]=(1ll*a[i-1]*basec+s[i]-'0')%cmod;
			for(int i=1;i<=len;i++)b[i]=(1ll*b[i-1]*based+s[i]-'0')%dmod;
			int ans=1;
			for(int i=1;i<=len-k+1;i++){
				int x=cM(a[i+k-1]-1ll*a[i-1]*E[k]%cmod+cmod);
				int y=dM(b[i+k-1]-1ll*b[i-1]*f[k]%dmod+dmod);
				int num=0;
				for(int j=first[x];j;j=e[j].from)if(e[j].v==y&&e[j].k==k){num=e[j].x;break;}
				ans=1ll*ans*num%MOD;
			}
			printf("%d\n",ans);
		}
	}
	return 0;
}
相關文章
相關標籤/搜索