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;
}