标签:std 单调队列 ems find register 距离 优化 blog href
官方题解(证明都在这)
神仙题鸭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;
}
标签:std 单调队列 ems find register 距离 优化 blog href
原文地址:https://www.cnblogs.com/smyjr/p/10264389.html