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;
    }
    
    天光渐亮。
  • 相关阅读:
    Linux实现ftp账号同时访问两个目录方法
    ubuntu系统中的VMware 安装win7 Ghost镜像的几个坑
    ubuntu14.04LTS安装vmware10.0.1
    翻页特效
    使用Fragment应用放置后台很久,被系统回收,出现crash
    Android软件安全开发实践(下)
    移动应用安全开发指南(Android)--完结篇(http://www.bubuko.com/infodetail-577312.html)
    数字签名与数字加密
    time_wait 原理分析和优化
    Go中http超时问题的排查
  • 原文地址:https://www.cnblogs.com/UntitledCpp/p/PKUSC2018.html
Copyright © 2011-2022 走看看