uoj#172. 【WC2016】论战捆竹竿(同余最短路、border性质) 题目描述 题解 同余最短路 border 题解 code

uoj#172. 【WC2016】论战捆竹竿(同余最短路、border性质)
题目描述
题解
同余最短路
border
题解
code

n<=5e5,w<=1e18,T=5

题解

需要各种前置姿势的吼题

同余最短路

求形如(sum a_ix_i=A;(a_i>=0,xin N))的A的个数

做法是按找%min(a)变成min(a)*x+b来分类跑最短路,f[i]表示%min(a)=i的最小的b,最后直接统计

本题中可以用aaa...aba...aaa的串来卡成n^2,所以要优化

border

定义next[i]为KMP中的next数组,当S[1...x]=S[|S|-x+1...S]时称S[1...x]为S的一个border

如果有S[i]=S[i+a],则a是S的一个周期,显然一个长度为x的border的周期为|S|-x,同理一个为x的周期一定对应一个长度为|S|-x的border

border的性♂质:一个串所有border的周期长度可以分成log个等差数列

证明:

对于长度>=|当前S|/2(小于一半会有问题)的border,设A是其中最长的(即周期最小),B是除A外任意一个

设p=|S|-|A|,q=|S|-|B|,则gcd(p,q)显然也是一个周期

因为gcd(p,q)<=|S|-|A|,所以有|S|-gcd(p,q)>=|A|

又因为A是最长的border,所以有|S|-gcd(p,q)<=|A|

因此|S|-gcd(p,q)=|A|,即gcd(p,q)=p,因此增加新的B不会产生新的周期,同时一定会形成一个等差数列(kp还是一个周期)

然后以<|S|/2的第一个border作为新的S,剩下的border也是新串的border,每次折半所以只有log个

题解

有了border的性质就可以优化最短路了,考虑一次处理一个等差数列x,x+p,...,x+lp

发现在%x意义下分成了gcd(x,p)个环,每个环互不影响

又发现环上的最小值不会被更新,所以从最小值处把环断开变成从长度为l的一段更新,把贡献拆开用单调队列维护

于是现在求出了当前等差数列的值,考虑转移到下一个等差数列

显然是f[i]->g[f[i]%x'],但是这样没有考虑刚好为x的转移,所以在g上做一个(x=0,p=x,l=inf)的同余最短路即可得到新的值

时间复杂度O(nlogn),Extra Test需要卡一下常

code

#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(a,b) (a<b?a:b)
#define ll long long
//#define file
using namespace std;

int nx[500001],A[500001],a[101][3],b[1000001],d[500001],T,n,i,j,k,l,tot,N,h,t;
ll D[500001],f[500001],g[500001],w,ans;
char st[500001];

int gcd(int x,int y) {int r=x%y; while (r) x=y,y=r,r=x%y; return y;}
void Work(ll x,ll p,int l)
{
	ll mn=9223372036854775807ll,s;
	int i,j,k,mn2;
	
	fo(i,1,N) if (f[b[i]]<mn) mn=f[b[i]],mn2=i;
	fo(i,1,mn2-1) b[i+N]=b[i];
	fo(i,1,N) b[i]=b[i+mn2-1];
	
	h=t=1;d[1]=1;D[1]=f[b[1]]-p;
	fo(i,2,N)
	{
		while (h<=t && (i-d[h])>=l) ++h;
		f[b[i]]=min(f[b[i]],f[b[d[h]]]+x+p*(i-d[h]));
		
		s=f[b[i]]-p*i;
		while (h<=t && D[t]>=s) --t;
		d[++t]=i;D[t]=s;
	}
}
void work(int n,ll x,ll p,int l)
{
	int i,j,k,K;
	
	K=gcd(n,p),N=n/K;
	fo(i,0,K-1)
	{
		k=i;
		fo(j,1,N) b[j]=k,k+=p,k-=(k>=n)?n:0;
		Work(x,p,l);
	}
}

int main()
{
	#ifdef file
	freopen("uoj172.in","r",stdin);
	#endif
	
	scanf("%d",&T);
	for (;T;--T)
	{
		scanf("%d%lld",&n,&w);
		scanf("%s",st+1);
		if (w<n) {printf("0
");continue;}
		
		fo(i,2,n)
		{
			nx[i]=nx[i-1];
			while (nx[i] && st[i]!=st[nx[i]+1])
			nx[i]=nx[nx[i]];
			nx[i]+=st[i]==st[nx[i]+1];
		}
		
		l=tot=0;
		for (i=nx[n]; i; i=nx[i]) A[++l]=n-i;A[++l]=n;
		j=0;
		fo(i,1,l)
		{
			j+=a[tot][1];
			if (j!=A[i])
			{
				++tot;
				a[tot][0]=A[i],a[tot][2]=1;
				if (i<l)
				a[tot][1]=A[i+1]-A[i],j=A[i+1],++i,a[tot][2]=2;
			}
			else ++a[tot][2];
		}
		
		memset(f,127,sizeof(f));
		f[n%a[1][0]]=n;
		fo(i,1,tot)
		{
			if (a[i][2]>1)
			work(a[i][0],a[i][0],a[i][1],a[i][2]);
			if (i<tot)
			{
				memset(g,127,sizeof(g));
				fo(j,0,a[i][0]-1)
				k=f[j]%(a[i+1][0]),g[k]=min(g[k],f[j]);
				
				memcpy(f,g,(a[i+1][0]+1)*8);
				work(a[i+1][0],0,a[i][0],n);
			}
		}
		
		ans=0;
		fo(i,0,a[tot][0]-1)
		if (f[i]<=w)
		ans=ans+((w-f[i])/a[tot][0]+1);
		printf("%lld
",ans);
	}
	
	fclose(stdin);
	fclose(stdout);
	return 0;
}