• cf Educational Round 123(Div. 2)


    C

    Description

    给定一个长度为n的数组,求在k(k=0...n)个不同位置加上x后,最大子段和。(子段可以为空)

    Solution

    f[i][j][0/1]表示前i个数加了至多k次x后的最大子段和,0/1表示a[i]有没有在该子段里。

    \(f[i][j][0]= \begin{cases} max\{f[i-1][j][0/1],f[i-1][j-1][0/1]\} & j>0 \\ max\{f[i-1][j][0/1]\}&j=0\end{cases}\)
    \(f[i][j][1]=a[i]+ \begin{cases} max\{f[i-1][j][1],f[i-1][j-1][1]+x,x\} & j>0 \\ max\{f[i-1][j][1]\}&j=0\end{cases}\)

    从大到小枚举j可以把[i]省掉。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=5005;
    int f[N][2],a[N],n,x;
    int main(){
    	int t;
    	scanf("%d",&t);
    	while(t--){
    		scanf("%d%d",&n,&x);
    		for(int i=1;i<=n;++i)
    			scanf("%d",&a[i]);
    		memset(f,0,sizeof(f));
    		for(int i=1;i<=n;++i){
    			for(int j=n;j>=1;--j){
    				f[j][0]=max(max(f[j][0],f[j-1][0]),max(f[j][1],f[j-1][1]));
    				f[j][1]=max(max(f[j][1],f[j-1][1]+x),x)+a[i];
    			}
    			f[0][0]=max(f[0][0],f[0][1]);
    			f[0][1]=max(f[0][1]+a[i],a[i]);
    		}
    		for(int j=0;j<=n;++j)
    			printf("%d ",max(f[j][0],f[j][1]));
    		printf("\n");
    	}
    	return 0;
    }
    

    D

    Description

    对于一个n\(\times\)m的网格,给定q个操作\(x_i,y_i\),每次可以选择将行\(x_i\)和列\(y_i\)涂成任意一种颜色,一共有k种颜色。求不同涂色的方案数。

    Solution

    只需确定每次操作对最终结果是否有影响,即存在一个格子到最后也没被后面的操作覆盖,那么本次操作对答案的贡献为k,最后根据乘法原理求幂。
    x[i]表示行i最后被涂色的时间,y[i]表示列i最后被涂色的时间。
    操作有效当且仅当行操作有效或列操作有效。
    行操作有效的条件:当前时间为行\(x_i\)最后被涂色的时间且存在一列的最后涂色时间在之前(t=x[\(x_i\)] and t>min{x[i]})。
    列操作同理。
    需要注意的是,本题维护x[i]要在\(O(q)\)时间内而非\(O(n)\)

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=200005,mod=998244353;
    struct query{
    	int x,y;
    }a[N];
    int x[N],y[N],n,m,k,q,cnt;
    ll power(ll x,int k){
    	ll ret=1;
    	while(k){
    		if(k&1) ret=ret*x%mod;
    		x=1ll*x*x%mod;k>>=1;
    	}
    	return ret;
    }
    int main(){
    	int t;
    	scanf("%d",&t);
    	while(t--){
    		scanf("%d%d%d%d",&n,&m,&k,&q);
    		memset(x,0,sizeof(x));
    		memset(y,0,sizeof(y));
    		for(int i=1;i<=q;++i)
    			scanf("%d%d",&a[i].x,&a[i].y);
    		int nn=n,mm=m; 
    		for(int i=q;i>=1;--i){
    			if(!x[a[i].x]) x[0]=x[a[i].x]=i,--nn;
    			if(!y[a[i].y]) y[0]=y[a[i].y]=i,--mm;
    		}
    		if(nn) x[0]=0;
    		if(mm) y[0]=0;
    		cnt=0;
    		for(int i=1;i<=q;++i)
    			if((i==x[a[i].x]&&i>=y[0])||(i==y[a[i].y]&&i>=x[0])) ++cnt;
    		printf("%lld\n",power(k,cnt));
    	}
    	return 0;
    }
    

    E

    Description

    对于一个n\(\times\)n的网格,只有向下走D和向右走R两种操作。
    现在给定一种走法序列,可以在最终不会走出整个网格的前提下,将序列的任意个操作无限重复。(任意次D->DD,R->RR)
    求这个序列的合法变化状态最终会覆盖到多少个网格。

    Solution

    考虑什么样的网格最终不会被覆盖到。
    对于每个网格,因为接下来要走R操作,下方的网格无法走到,那么这时会有x个下方网格无法走到。
    如果在这之前没有D操作,那么会有x=n-1;如果在这之前有D操作,那么x=在这之后的D操作个数。
    D操作的右边网格同理。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=200005;
    int n,l,D,R;
    long long ans;
    bool d,r;
    char s[N];
    int main(){
    	int t;
    	scanf("%d",&t);
    	while(t--){
    		scanf("%d",&n);
    		scanf("%s",s+1);
    		D=R=0;l=strlen(s+1);
    		for(int i=1;i<=l;++i){
    			if(s[i]=='R') ++R;
    			else ++D;
    		}
    		if(!D||!R){
    			printf("%d\n",n);
    			continue;
    		}
    		ans=1ll*n*n;d=r=false;
    		for(int i=1;i<=l;++i){
    			if(s[i]=='R'){
    				--R;r=true;
    				if(!d) ans-=n-1;
    				else ans-=D;
    			}
    			else{
    				--D;d=true;
    				if(!r) ans-=n-1;
    				else ans-=R;
    			}
    		}
    		printf("%lld\n",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    如何:将控件锁定到 Windows 窗体
    Linux 设置字符集
    sql 批量处理
    解决表被锁了
    oracle 分页模板
    创建用户及表空间
    恢复数据库数据
    instr vs like 效率
    自定义参数转换器
    spring boot 整合MyBatis
  • 原文地址:https://www.cnblogs.com/AireenYe/p/cfEducationalRound123.html
Copyright © 2020-2023  润新知