zoukankan      html  css  js  c++  java
  • PKUSC2018

    最大前缀和

    如果钦定某个位置为最大前缀和,设位置为 (pos) ,那么必然也是 ([1,pos]) 的最大前缀,并且 ([pos+1,n]) 的前缀和均 (<=0) .

    (nleq 20) 可以考虑状压。设 (sum[S]) 表示前半部分选择状态为 (S) 时,前半部分序列总和,(f[S]) 表示状态为 (S) 时前半部分的方案数,(g[S]) 表示状态为 (S) 时后半部分的方案数。转移见代码。 完整代码

    注意一下这边 (f) 数组的贡献方式,当且仅当 (f[S]) 合法(就是 (sum[S]ge 0) ) 时才能向 (f[S|(1<<i)]) 贡献。

    //Author: RingweEH
    int main()
    {
    	n=read();
    	for ( int i=1; i<=n; i++ ) a[i]=read(),f[1<<(i-1)]=1;
    
    	int lim=1<<n; g[0]=1;
    	for ( int S=0; S<lim; S++ ) 
    		for ( int i=0; i<n; i++ )
    			if ( S&(1<<i) ) sum[S]+=a[i+1];
    	for ( int S=0; S<lim; S++ )
    		if ( sum[S]>=0 )
    		{ for ( int i=0; i<n; i++ ) if ( !(S&(1<<i)) ) bmod(f[S|(1<<i)],f[S]); }
    		else for ( int i=0; i<n; i++ ) if ( S&(1<<i) ) bmod(g[S],g[S^(1<<i)]);
    
    	ll ans=0;
    	for ( int S=0; S<lim; S++ ) bmod(ans,sum[S]*f[S]%Mod*g[(lim-1)^S]%Mod);
    	printf("%lld
    ",ans );
    
    	return 0;
    }
    

    真实排名

    • 如果第 (i) 个人的成绩( (x) )不变,那么能变动的是:原本就比他高的,和比他小且变了之后还是比他小的。剩下的一个不能动。

    • 如果第 (i) 个人的成绩变动,(xleq a_j< 2x) 都必须要两倍。这些是强制要变,剩下可以随便动。

    对于第一种情况,不能动的区间是连续的一段;对于第二种,可以随意动的是一个连续区间,因此可以排序后双指针,(mathcal{O}(nlog n)) .

    写挂了 /wq 理解了一发 @zkdxl 的代码 Orz 完整代码

    之前有一发忘记判组合数负数结果上洛谷就 ub 了 /kk

    int main()
    {
    	n=read(); k=read(); C_Init(N-10);
    	for ( int i=1; i<=n; i++ ) a[i].val=read(),a[i].id=i;;
    
    	sort(a+1,a+1+n);		//从大到小
    	int las=1;
    	for ( int i=2; i<=n; i++ ) 
    		if ( a[i].val!=a[i-1].val ) 
    		{
    			for ( int j=las; j<i; j++ ) bnd[j]=i-1;
    			las=i;
    		}
    	for ( int j=las; j<=n; j++ ) bnd[j]=n;
    
    	for ( int i=n,le=n; i; i-- )
    	{
    		while ( a[i].val>a[le].val*2 && le ) le--;
    		nxt[i]=le+1;
    	}
    	for ( int i=1,ri=1; i<=n; i++ )
    	{
    		while ( a[i].val*2<=a[ri].val && ri<=n ) ri++;
    		pre[i]=ri-1;
    	}
    	for ( int i=1; i<=n; i++ )
    		if ( (i>1) && a[i].val==a[i-1].val ) ans[a[i].id]=ans[a[i-1].id];
    		else if ( a[i].val==0 ) ans[a[i].id]=C(n,k);
    		else
    		{
    			int x=bnd[i]-i,y=i-pre[i],z=nxt[i]-i;
    			//x: count(a[j]=a[i])-1
    			//y: a[i]*2>a[j]
    			//z: a[j]*2>=a[i]
    			bmod(ans[a[i].id],C(n-z+x,k));
    			bmod(ans[a[i].id],C(n-y-x,k-y-x));
    		}
    
    	for ( int i=1; i<=n; i++ ) printf("%d
    ",ans[i] );
    
    	return 0;
    }
    

    神仙的游戏

    首先一个很显然的事情是,如果存在一个长度为 (x) 的 border ,总长度为 (n) ,那么 (n-x) 一定是一个合法的循环节长度(不一定刚好整除)。进而有:如果 (iequiv j pmod{n-x})(s[i],s[j]) 分别是 (0,1) ,那么 (x) 显然不是合法的 border 。

    也就是说,原串的 ? 其实没用,所有的 01 都是一种限制。一个暴力的做法是直接枚举原串所有的位置对得出 01 的限制,进而求出合法的 border 。

    考虑优化。令 (displaystyle F(x)=sum_{i=0}^{n-1}[s_i=0]x^i,G(x)=sum_{i=0}^{n-1}[s_i=1]x^{-i}) ,那么 (f(i)) 的生成函数就是 (F(x)*G(x)) . 由于 (G) 的幂次全是负数,所以整体平移即可。完整代码 (最优解第一页 /se

    //Author: RingweEH
    using Poly::Poly_Mul;
    
    int n,F[M],G[M];
    char s[N];
    
    int main()
    {
    	scanf("%s",s); n=strlen(s);
    
    	for ( int i=0; i<n; i++ ) F[i]=(s[i]=='0'),G[i]=(s[n-1-i]=='1');
    	Poly_Mul(n,n,F,G,F);
    	ll ans=1ll*n*n;
    	for ( int i=1; i<n; i++ )
    	{
    		bool fl=0;
    		for ( int j=i; j<n; j+=i ) fl|=(F[n-1+j]+F[n-1-j]>0);
    		if ( !fl ) ans^=(1ll*(n-i)*(n-i));
    	}
    
    	printf("%lld
    ",ans );
    
    	return 0;
    }
    

    星际穿越

    没看到 (l<r<x) . /fn

    要计算 (dfrac1{r_i-l_i+1}displaystylesum_{y=l_i}^{r_i} dis(x_i,y)) ,一个 (O(n^2)) 的做法是预处理出两两距离,然后做一遍前缀和直接相减。设 (f[i][j]) 是以 (i) 为起点,走 (j) 步能到达的左端点(注意有一次向右的可能性),每次用 (kge f[i][j-1])(l[k]) 更新 (f[i][j+1]) ,并且 (dis[i][k]=j) ,直到 (f[i][j-1]=1) 。这样能有 (70pts) ,代码如下:

    mn[n+1]=n+1;
    for ( int i=n; i; i-- ) mn[i]=min(mn[i+1],l[i]);
    for ( int i=1; i<=n; i++ ) f[i][0]=i,f[i][1]=l[i],f[i][2]=mn[i+1];
    for ( int i=2,k,j; i<=n; i++ )
    {
        k=i-1;
        for ( j=1; f[i][j]>1; j++ )
            for ( ; k>=f[i][j]; k-- )
                bmin(f[i][j+1],l[k]),dis[i][k]=j;
        for ( ; k; k-- ) dis[i][k]=j;
    }    
    

    然后倍增优化。设 (f[i][j]) 表示 ([i,n]) 内节点走 (2^j) 步能到达的左端点,有

    [f[i][0]=min(l_k),kin [i,n]\\ f[i][j]=f[f[i][j-1]][j-1] ]

    再维护一个 (g[i][j]) 表示 (i)([f[i][j],i-1]) 内所有点的距离和。那么

    [g[i][0]=i-f[i][0]\\ g[i][j]=g[i][j-1]+g[f[i][j-1]][j-1]+2^{j-1}(f[i][j-1]-f[i][j]) ]

    好高啊 /ll 连倍增都不会了 /wq 完整代码

    //Author: RingweEH
    void Init()
    {
    	lg=log2(n)+1; powe[0]=1;
    	for ( int i=1; i<=lg; i++ ) powe[i]=powe[i-1]*2;
    	f[n][0]=l[n];
    	for ( int i=n-1; i>=1; i-- )
    		f[i][0]=min(f[i+1][0],l[i]),g[i][0]=i-f[i][0];
    	for ( int j=1; j<=lg; j++ )
    		for ( int i=1; i<=n; i++ )
    			if ( f[i][j-1] )
    			{
    				f[i][j]=f[f[i][j-1]][j-1];
    				g[i][j]=g[i][j-1]+g[f[i][j-1]][j-1]+(f[i][j-1]-f[i][j])*powe[j-1];
    			}
    }
    
    int Calc( int x,int le ) //answer in [l,x-1]
    {
    	if ( l[x]<=le ) return x-le;
    	int res=x-l[x],nw=1; x=l[x];
    	for ( int i=lg; i>=0; i-- )
    		if ( f[x][i]>=le )
    		{
    			res+=g[x][i]+nw*(x-f[x][i]);
    			nw+=(1<<i); x=f[x][i];
    		}
    	if ( x>le ) res+=x-le+nw*(x-le);
    	return res;
    }
    
    天光渐亮。
  • 相关阅读:
    三次请求(读-改-读)引出nibernate 一级缓存
    算法竞赛入门经典第一、二章摘记
    uva 10905 Children's Game
    uva 11205 The broken pedometer
    uva 10160 Servicing stations
    uva 208 Firetruck
    uva 167 The Sultan's Successors
    zoj 1016 Parencodings
    uva 307 Sticks
    uva 216 Getting in Line
  • 原文地址:https://www.cnblogs.com/UntitledCpp/p/PKUSC2018.html
Copyright © 2011-2022 走看看