码迷,mamicode.com
首页 > 其他好文 > 详细

luogu P4156 [WC2016]论战捆竹竿

时间:2019-01-13 23:23:54      阅读:206      评论:0      收藏:0      [点我收藏+]

标签: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;
}

luogu P4156 [WC2016]论战捆竹竿

标签:std   单调队列   ems   find   register   距离   优化   blog   href   

原文地址:https://www.cnblogs.com/smyjr/p/10264389.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!