【LuoguP4156】论战捆竹竿

题目链接

题意简述

你有一个长度为 n 的字符串 , 将它复制任意次 , 复制出的串的前缀可以与之前的串的后缀重叠在一起 , 问最后总共可能的长度数目 , 长度不能超过 (w)
多组数据。

(nleq 5*10^5 ,wleq 10^{18})

Sol

显然每次可以重叠的部分是原串的一个 boder
假设这个boder长度为 (L) , 那么长度可以只增加 (n-L)
那么就是一个存在性完全背包问题。
那么先求出所有boder , 之后显然可以用同余类最短路来求答案。
不过这个样子复杂度太高了。
最短路的做法是有所多余的 , 每一个点只会向后连边 , 连边的模式也是一定的 , 最短路和转移之间的顺序就是没有关系的 , 我们可以分开考虑每一种连边方式 , 假设这个长度为 (L)
由数学知识我们可以知道他将原来的长度为 (n) 的数组分成了 (gcd(n,L)) 个在这次转移中互不影响的环 , 我们在每一个环里找到 距离最小的那个点开始转移就可以了。
这样复杂度就是 (O(T*n^2)) , 只有 30'

接下来就要用到一个很强的性质了 , 一个字符串的 (boder) 形成的等差数列的个数不会超过 (log)
证明不会。

这又什么用呢?
考虑一个等差数列在转移的时候有什么优秀的性质。
我们假设首项为(x),公差为 (d) ,长度为 (l)
那么假设我们在模 (x) 的意义下进行这些转移 , 这些长度都可以等价于一个长度为 (d) 的转移方式!这样我们要考虑的转移方式总数就下降到了 (log) 级别。由于长度只有 (l) ,我们转移的时候最多就只能从前 (l-1) 个转移过来 , 用一个单调队列来维护转移即可。
但是这样还有一个问题 , 因为这样同余类最短路的模数不就一直在变?
其实是可以转换的 , 具体方式为:
假设新的模数 (p) 下的dp数组为 (g) , 原来的模数为 (q) ,数组为 (f) ,那么首先可以 (g[f[i]\%p]=f[i]) , 这样有什么问题呢?
容易发现 , 由于之前我们相当于是没有进行 长度为 (q) 的转移的 , 所以这里的 dp 数组会有些值是不对的 , 这样的话我们重新进行一次长度为 (q) 的转移就好了。

code:

#include<bits/stdc++.h>
using namespace std;
#define next NEXTT
#define Set(a,b) memset(a,b,sizeof(a))
#define Copy(a,b) memcpy(a,b,sizeof(a))
const int N=5e5+10;
typedef long long ll;
char S[N];
int next[N];
int T;
ll n,w,ans,f[N],g[N];
int len[N],tot=0,Idex,vis[N];
int head,tail;
int que[N],V[N];
inline void Update(int pL,int nL,int d,int cnt){
	Set(g,-1);
	for(int i=0;i<pL;++i) if(~f[i]) {int res=f[i]%nL;if(~g[res]) g[res]=min(g[res],f[i]);else g[res]=f[i];}
	Copy(f,g);
	++Idex;
	for(int i=0;i<nL;++i) {
		if(vis[i]==Idex) continue;
		int mip=i;vis[i]=Idex;
		for(int j=(i+pL)%nL;j^i;j=(j+pL)%nL) {
			if((!~f[mip])||((~f[j])&&f[mip]>f[j])) mip=j;
			vis[j]=Idex;
		}
		if(~f[mip]){
			for(int pre=mip,j=(mip+pL)%nL;j^mip;pre=j,j=(j+pL)%nL){if(!~f[j]) f[j]=f[pre]+pL;else f[j]=min(f[j],f[pre]+pL);}
		}
	}++Idex;
	for(int i=0;i<nL;++i) {
		if(vis[i]==Idex) continue;
		int mip=i;vis[i]=Idex;
		for(int j=(i+d)%nL;j^i;j=(j+d)%nL){
			if((!~f[mip])||((~f[j])&&f[j]<f[mip])) mip=j;
			vis[j]=Idex;
		}
		if(!~f[mip]) continue;
		head=1,tail=1;que[1]=1;V[1]=mip;
		for(int k=2,j=(mip+d)%nL;j^mip;j=(j+d)%nL,++k){
			while(head<=tail&&k-que[head]>=cnt) ++head;
			if(head<=tail) {if(~f[j]) f[j]=min(f[j],f[V[head]]+(k-que[head])*d+nL);else f[j]=f[V[head]]+(ll)(k-que[head])*d+nL;}
			if(~f[j]){
				while(head<=tail&&f[j]<=(ll)d*(k-que[tail])+f[V[tail]]) --tail;
				que[++tail]=k,V[tail]=j;
			}
		}
	}
	return;
}
void Solve(){
	int lst=n;--tot;
	reverse(len+1,len+1+tot);
	int L;
	for(int i=1,j;i<=tot;i=j) {
		j=i+1;int d=len[i]-len[j];
		if(j>tot) d=0;
		while(j<=tot&&len[j-1]-len[j]==d) ++j;
		L=len[j-1];Update(lst,L,d,j-i);lst=L;
	}
	ans=0;
	for(int j=0;j<lst;++j) if(~f[j]&&f[j]<=w) ans+=(w-f[j])/lst+1;
	return;
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("jie.in","r",stdin);
	freopen("jie.out","w",stdout);
#endif
	scanf("%d",&T);
	while(T--){
		Set(vis,0);Idex=0;
		scanf("%lld %lld",&n,&w);
		ans=0;w-=n;scanf("%s",S+1);
		register int i,j=0;Set(next,0);
		for(i=2;i<=n;++i) {
			while(j&&S[i]!=S[j+1]) j=next[j];
			if(S[i]==S[j+1]) ++j;next[i]=j;
		}j=n;tot=0;
		while(j){len[++tot]=n-next[j];j=next[j];}
		Set(f,-1),f[0]=0,Solve();
		printf("%lld
",ans);
	}
}


原文地址:https://www.cnblogs.com/NeosKnight/p/10397313.html