luogu P4156 [WC2016]论战捆竹竿

upd:19.12.19

重新写了这道题,并且修正了原来题解描述中一些奇怪的东西

然后重新发出来假装根了脖


luogu

uoj

官方题解(证明都在这)

神仙题鸭qwq

转化模型,如果原串存在一个长度为\(i(i\(\mathrm{border}\),那么每次可以往后面加\(n-i\)的长度,所以这题本质是有一个集合(这题里\(n\)也属于这个集合),每次可以往后面加上集合内的一个数,问在给定范围内能凑出来的数的数量

这显然是同余类最短路,枚举集合中一个数\(a_1\)作为模数,然后求出\(di_i\)表示模\(a_1\)意义下为\(i\)的数中能凑出来的最小数

这个暴力是可以优化成\(O(n|S|)\)的.具体操作是枚举每个数\(a_i\),然后只用这个数往后跳,这样在膜\(m\)意义下可以形成\(gcd(a_i,m)\)个环.对每个环找到\(di\)最小的点,从这个点开始依次遍历整个环,更新后一个位置

有个结论是集合中的数(也就是所有合法\(\mathrm{border}\)长度)可以分成\(logn\)个等差数列,所以可以枚举每个等差数列贡献答案.具体的设某个等差序列首项为\(b\),公差为\(d\),项数为\(l\),然后当前同余最短路模数为\(a\),先把模\(a\)意义的最短路转成模\(b\)意义的最短路.具体操作是用模\(a\)意义下的所有\(di\)更新模\(b\)意义下的一些\(di\),然后从每个环的最小\(di\)处以\(a\)为跳跃距离更新其他的点.对于公差的贡献,相当于每个位置以\(d\)作为跳跃距离往后跳最多\(l-1\)次,然后转移跳到的位置,转移大概为\(\forall k \in [1,l-1],di_{(x+dk)\% b}=\min(di_{(x+dk)\% b},di_x+b+dk)\),这个东西用个单调队列优化转移即可.

#include
#define LL long long
#define uLL unsigned long long

using namespace std;
const int N=5e5+10;
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 ad(int x,int y,int z){x+=y,x-=x>=z?z:0;return x;}    //加上这个就能过extest了~~然后垫底~~
int gcd(int a,int b){return b?gcd(b,a%b):a;}
uLL ha[N],hb[N],b1=233,hha[N],hhb[N],b2=677,mod=993244853;
int n,a[N],m,hd,tl;
LL di[N],d2[N],ln,q[N][2];
char cc[N];

int main()
{
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
    int T=rd();
    while(T--)
    {
        n=rd(),ln=rd()-n;
        scanf("%s",cc+1);
        for(int i=1;i<=n;++i)
            ha[i]=ha[i-1]*b1+cc[i],hha[i]=(1ll*hha[i-1]*b2%mod+cc[i])%mod;
        uLL p1=1,p2=1;
        hb[n+1]=hhb[n+1]=0;
        for(int i=n;i;--i)
        {
            hb[i]=hb[i+1]+cc[i]*p1,hhb[i]=(hhb[i+1]+1ll*cc[i]*(int)p2%mod)%mod;
            p1*=b1,p2=p2*b2%mod;
        }
        m=0;
        for(int i=n-1;i;--i)
            if(ha[i]==hb[n-i+1]&&hha[i]==hhb[n-i+1]) a[++m]=n-i;
        int sz=n;
        memset(di,0x3f3f3f,sizeof(LL)*(n+1));
        di[0]=0;
        for(int l=1,r=1+(m>1);l<=m;l=r+1,r=r+2)
        {
            r=min(r,m);
            int dx=a[l+1]-a[l];
            while(rdi[j]) np=j;
                for(int j=ad(np,zz,a[l]),ls=np;j!=np;j=ad(j,zz,a[l]),ls=ad(ls,zz,a[l]))
                    di[j]=min(di[j],di[ls]+sz);
            }
            sz=a[l];
            if(l==r) continue;
            jp=gcd(sz,dx),zz=dx%sz;
            for(int i=0;idi[j]) np=j;
                hd=1,q[tl=1][0]=di[np],q[tl][1]=0;
                for(int j=ad(np,zz,sz),k=1;j!=np;j=ad(j,zz,sz),++k)
                {
                    while(hd<=tl&&k-q[hd][1]>r-l) ++hd;
                    if(hd<=tl) di[j]=min(di[j],q[hd][0]+sz+1ll*dx*k);
                    while(hd<=tl&&q[tl][0]>=di[j]-1ll*dx*k) --tl;
                    q[++tl][0]=di[j]-1ll*dx*k,q[tl][1]=k;
                }
            }
        }
        LL ans=0;
        for(int i=0;i

你可能感兴趣的:(luogu P4156 [WC2016]论战捆竹竿)