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;
}
posted @ 2022-02-27 20:13  Aireen_Ye  阅读(20)  评论(0编辑  收藏  举报
底部 顶部 留言板 归档 标签
Der Erfolg kommt nicht zu dir, du musst auf den Erfolg zugehen.