luogu P4156 [WC2016]論戰捆竹竿
阿新 • • 發佈:2019-01-13
官方題解(證明都在這)
神仙題鴨qwq
轉化模型,發現這題本質就是一個集合,每次可以加上集合裡的數,問可以拼出多少不同的數
首先暴力需要膜意義下的最短路,例題戳這
然後這個暴力可以優化成N^2的.具體操作是列舉每個數,然後從某個點只用這個數往後跳,這樣在膜m意義下可以形成\(gcd(a,m)\)個環.每個環找到dis最小的點,從這個點開始依次遍歷整個環,更新後一個位置
有個結論是集合中的數可以分成\(logn\)個等差數列,所以可以每個等差數列貢獻答案
然後對於每個等差數列,先把膜m意義轉成膜\(a_1\)的最短路.具體操作是對用已知dis值更新部分答案,然後從最小dis處一直往後跳m更新其他的點.然後對於每個位置可以從前面的一段區間轉移過來(代價為距離*公差),可以單調佇列優化
// luogu-judger-enable-o2 #include<bits/stdc++.h> #define LL long long #define uLL unsigned long long #define il inline #define re register using namespace std; const int N=5e5+10; il LL rd() { LL x=0,w=1;char ch=0; while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();} return x*w; } int m,a[N]; char cc[N]; LL n,w,di[N],dd[N]; uLL hl[N],hr[N],bb[N],bs=377; int ff[N]; il int findf(int x){return ff[x]==x?x:ff[x]=findf(ff[x]);} il int gcd(int a,int b){return b?gcd(b,a%b):a;} LL q[N][2],hd,tl; il void cg(LL *di,int i,int k,int md,LL cw,LL ww,int mm) { hd=1,q[tl=1][0]=di[i]-ww,q[tl][1]=1; LL jj=2; for(int j=(i+k)%md;j^i;j=(j+k)%md,++jj) { while(hd<=tl&&jj-q[hd][1]+1>mm) ++hd; di[j]=di[j]<q[hd][0]+1ll*jj*ww+cw?di[j]:q[hd][0]+1ll*jj*ww+cw; while(hd<=tl&&q[tl][0]>=di[j]-1ll*jj*ww) --tl; q[++tl][0]=di[j]-1ll*jj*ww,q[tl][1]=jj; } } int main() { bb[0]=1; for(int i=1;i<=N-10;++i) bb[i]=bb[i-1]*bs; int T=rd(); memset(dd,100,sizeof(dd)); while(T--) { n=rd(),w=rd()-n; scanf("%s",cc+1); for(int i=1;i<=n;++i) hl[i]=hl[i-1]*bs+cc[i]; hr[n+1]=0; for(int i=n;i;--i) hr[i]=bb[n-i]*cc[i]+hr[i+1]; m=0; for(int i=1;i<n;++i) if(hl[n-i]==hr[i+1]) a[++m]=i; for(int i=1;i<=m+2;++i) ff[i]=i; ff[0]=1; memset(di,0x3f3f3f,sizeof(di)); di[0]=0; LL md=n; for(int cn=0;cn<m;) { int ii=findf(0),k=1,dt=0; ++cn,ff[ii]=ii+1; for(int j=findf(ii);j<=m;j=findf(j)) { if(!dt) dt=a[j]-a[ii],++cn,++k,ff[j]=j+1; else if(a[j]==a[ii]+k*dt) ++cn,++k,ff[j]=j+1; else break; } LL x=a[ii]; for(int i=0;i<md;++i) dd[di[i]%x]=min(dd[di[i]%x],di[i]),di[i]=di[n+1]; int sht=gcd(md,x); for(int i=0;i<sht;++i) { int jj=i; for(int j=(jj+md)%x;j^i;j=(j+md)%x) if(dd[j]<dd[jj]) jj=j; for(int j=jj,k=(jj+md)%x;k^jj;j=(j+md)%x,k=(k+md)%x) dd[k]=min(dd[k],dd[j]+md); } md=x; for(int i=0;i<md;++i) di[i]=dd[i],dd[i]=dd[n+1]; sht=gcd(md,dt); for(int i=0;i<sht;++i) { int jj=i; for(int j=(jj+dt)%md;j^i;j=(j+dt)%md) if(di[j]<di[jj]) jj=j; cg(di,jj,dt,md,md,dt,k); } } LL ans=0; for(int i=0;i<md;++i) ans+=max(1ll*0,(w-di[i]+md)/md); printf("%lld\n",ans); } return 0; }