zoukankan      html  css  js  c++  java
  • luogu P4156 [WC2016]论战捆竹竿

    upd:19.12.19

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

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


    luogu

    uoj

    官方题解(证明都在这)

    神仙题鸭qwq

    转化模型,如果原串存在一个长度为(i(i<n))(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<bits/stdc++.h>
    #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(r<m&&dx==a[r+1]-a[r]) ++r;
    			memset(d2,0x3f3f3f,sizeof(LL)*(a[l]+1));
    			for(int i=0;i<sz;++i)
    				d2[di[i]%a[l]]=min(d2[di[i]%a[l]],di[i]);
    			memcpy(di,d2,sizeof(LL)*(a[l]+1));
    			int jp=gcd(sz,a[l]),zz=sz%a[l];;
    			for(int i=0;i<jp;++i)
    			{
    				int np=i;
    				for(int j=ad(i,zz,a[l]);j!=i;j=ad(j,zz,a[l]))
    					if(di[np]>di[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;i<jp;++i)
    			{
    				int np=i;
    				for(int j=ad(i,zz,sz);j!=i;j=ad(j,zz,sz))
    					if(di[np]>di[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<sz;++i) ans+=max((ln-di[i]+sz)/sz,0ll);
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    覆盖式发布与非覆盖式发布
    GIT
    Web Service返回符合Xml Schema规范的Xml文档
    下拉渐显菜单
    计算网页上坐标的距离
    初识交互设计
    良好用户体验-实现过程!
    做 用户调研?
    这个没什么技术含量,实现起来很简单?
    SQL SERVER 登录问题!该用户与可信的Sql Server连接无关联
  • 原文地址:https://www.cnblogs.com/smyjr/p/10264389.html
Copyright © 2011-2022 走看看