zoukankan      html  css  js  c++  java
  • uoj#172. 【WC2016】论战捆竹竿(同余最短路、border性质)

    题目描述

    n<=5e5,w<=1e18,T=5

    题解

    需要各种前置姿势的吼题

    同余最短路

    求形如(sum a_ix_i=A;(a_i>=0,xin N))的A的个数

    做法是按找%min(a)变成min(a)*x+b来分类跑最短路,f[i]表示%min(a)=i的最小的b,最后直接统计

    本题中可以用aaa...aba...aaa的串来卡成n^2,所以要优化

    border

    定义next[i]为KMP中的next数组,当S[1...x]=S[|S|-x+1...S]时称S[1...x]为S的一个border

    如果有S[i]=S[i+a],则a是S的一个周期,显然一个长度为x的border的周期为|S|-x,同理一个为x的周期一定对应一个长度为|S|-x的border

    border的性♂质:一个串所有border的周期长度可以分成log个等差数列

    证明:

    对于长度>=|当前S|/2(小于一半会有问题)的border,设A是其中最长的(即周期最小),B是除A外任意一个

    设p=|S|-|A|,q=|S|-|B|,则gcd(p,q)显然也是一个周期

    因为gcd(p,q)<=|S|-|A|,所以有|S|-gcd(p,q)>=|A|

    又因为A是最长的border,所以有|S|-gcd(p,q)<=|A|

    因此|S|-gcd(p,q)=|A|,即gcd(p,q)=p,因此增加新的B不会产生新的周期,同时一定会形成一个等差数列(kp还是一个周期)

    然后以<|S|/2的第一个border作为新的S,剩下的border也是新串的border,每次折半所以只有log个

    题解

    有了border的性质就可以优化最短路了,考虑一次处理一个等差数列x,x+p,...,x+lp

    发现在%x意义下分成了gcd(x,p)个环,每个环互不影响

    又发现环上的最小值不会被更新,所以从最小值处把环断开变成从长度为l的一段更新,把贡献拆开用单调队列维护

    于是现在求出了当前等差数列的值,考虑转移到下一个等差数列

    显然是f[i]->g[f[i]%x'],但是这样没有考虑刚好为x的转移,所以在g上做一个(x=0,p=x,l=inf)的同余最短路即可得到新的值

    时间复杂度O(nlogn),Extra Test需要卡一下常

    code

    #include <bits/stdc++.h>
    #define fo(a,b,c) for (a=b; a<=c; a++)
    #define fd(a,b,c) for (a=b; a>=c; a--)
    #define min(a,b) (a<b?a:b)
    #define ll long long
    //#define file
    using namespace std;
    
    int nx[500001],A[500001],a[101][3],b[1000001],d[500001],T,n,i,j,k,l,tot,N,h,t;
    ll D[500001],f[500001],g[500001],w,ans;
    char st[500001];
    
    int gcd(int x,int y) {int r=x%y; while (r) x=y,y=r,r=x%y; return y;}
    void Work(ll x,ll p,int l)
    {
    	ll mn=9223372036854775807ll,s;
    	int i,j,k,mn2;
    	
    	fo(i,1,N) if (f[b[i]]<mn) mn=f[b[i]],mn2=i;
    	fo(i,1,mn2-1) b[i+N]=b[i];
    	fo(i,1,N) b[i]=b[i+mn2-1];
    	
    	h=t=1;d[1]=1;D[1]=f[b[1]]-p;
    	fo(i,2,N)
    	{
    		while (h<=t && (i-d[h])>=l) ++h;
    		f[b[i]]=min(f[b[i]],f[b[d[h]]]+x+p*(i-d[h]));
    		
    		s=f[b[i]]-p*i;
    		while (h<=t && D[t]>=s) --t;
    		d[++t]=i;D[t]=s;
    	}
    }
    void work(int n,ll x,ll p,int l)
    {
    	int i,j,k,K;
    	
    	K=gcd(n,p),N=n/K;
    	fo(i,0,K-1)
    	{
    		k=i;
    		fo(j,1,N) b[j]=k,k+=p,k-=(k>=n)?n:0;
    		Work(x,p,l);
    	}
    }
    
    int main()
    {
    	#ifdef file
    	freopen("uoj172.in","r",stdin);
    	#endif
    	
    	scanf("%d",&T);
    	for (;T;--T)
    	{
    		scanf("%d%lld",&n,&w);
    		scanf("%s",st+1);
    		if (w<n) {printf("0
    ");continue;}
    		
    		fo(i,2,n)
    		{
    			nx[i]=nx[i-1];
    			while (nx[i] && st[i]!=st[nx[i]+1])
    			nx[i]=nx[nx[i]];
    			nx[i]+=st[i]==st[nx[i]+1];
    		}
    		
    		l=tot=0;
    		for (i=nx[n]; i; i=nx[i]) A[++l]=n-i;A[++l]=n;
    		j=0;
    		fo(i,1,l)
    		{
    			j+=a[tot][1];
    			if (j!=A[i])
    			{
    				++tot;
    				a[tot][0]=A[i],a[tot][2]=1;
    				if (i<l)
    				a[tot][1]=A[i+1]-A[i],j=A[i+1],++i,a[tot][2]=2;
    			}
    			else ++a[tot][2];
    		}
    		
    		memset(f,127,sizeof(f));
    		f[n%a[1][0]]=n;
    		fo(i,1,tot)
    		{
    			if (a[i][2]>1)
    			work(a[i][0],a[i][0],a[i][1],a[i][2]);
    			if (i<tot)
    			{
    				memset(g,127,sizeof(g));
    				fo(j,0,a[i][0]-1)
    				k=f[j]%(a[i+1][0]),g[k]=min(g[k],f[j]);
    				
    				memcpy(f,g,(a[i+1][0]+1)*8);
    				work(a[i+1][0],0,a[i][0],n);
    			}
    		}
    		
    		ans=0;
    		fo(i,0,a[tot][0]-1)
    		if (f[i]<=w)
    		ans=ans+((w-f[i])/a[tot][0]+1);
    		printf("%lld
    ",ans);
    	}
    	
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    HDMI速率计算
    HDMI各版本对比
    HDMI
    MOS管驱动详解
    Allegro16.6和17.0和17.2中将板框导出DXF文件
    allegro设置鼠标滚轮放大缩小
    MOS简介
    Allegro设置十字大光标
    笔记09
    笔记09 WS,WCF
  • 原文地址:https://www.cnblogs.com/gmh77/p/13235950.html
Copyright © 2011-2022 走看看