AtCoder Regular Contest 157
期末考完的晚上打的复健场,确实很适合复健,前四题都比较暴力。
好消息是头一次在ARC中做出了四题,坏消息是unrated了,而且rated了下一场ABC,然后翻车掉分了。
不过罚时惨烈,思路不太严密。
A
签到,发现b和c之差最多为1,并且是充分的,但需要特判一段全为X或Y(原本没想到,-1)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
//srand(time(0));
//freopen("1.in","r",stdin);
//freopen("1.out","w",stdout);
int n,a,b,c,d;
cin>>n>>a>>b>>c>>d;
if(abs(b-c)>1 || (!b && !c && (a && d))) puts("No");
else puts("Yes");
return 0;
}
B
思路是自然的:首先把中间那些B的段改成A,贪心从长度小的开始选即可;然后改两端的。但如果把全部都变成B之后仍有剩余,那只能把一些原本是A的改成B,这个的贪心策略和原来的正好相反。(特判顺序没处理好,-2)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,k;
char a[N];
int lt=0,mn=n+1,mx=0,c=0,ans=0;
void work(){
priority_queue<int>q2;
lt=0,mn=n+1,mx=0,c=0;
for(int i=1;i<=n;i++) if(a[i]=='X'){
c++;
mx=max(mx,i);
mn=min(mn,i);
if(lt && lt<i-1) q2.push(i-lt-1);//cout<<i-lt-1<<endl;
lt=i;
}
ans=n-1;
k-=c;
int t=mn-1+n-mx;
//cout<<"t="<<t<<" k="<<k<<endl;
if(t>=k){
cout<<ans-k<<endl;
return;
}
ans-=t; k-=t;
while(k && !q2.empty()){
int u=q2.top();
q2.pop();
if(u>=k){
cout<<ans-k-1<<endl;
return;
}
k-=u;
ans-=u+1;
}
cout<<ans<<endl;
}
priority_queue<int,vector<int>,greater<int> >q;
int main()
{
cin>>n>>k;
scanf("%s",a+1);
for(int i=1;i<=n;i++) if(a[i]=='Y'){
c++;
mx=max(mx,i);
mn=min(mn,i);
if(lt && lt<i-1) q.push(i-lt-1);
lt=i;
if(i>1 && a[i-1]=='Y') ans++;
}
if(!c){
cout<<max(k-1,0)<<endl;
return 0;
}
if(c==n){
cout<<max(n-k-1,0)<<endl;
return 0;
}
if(c+k>n){
work();
return 0;
}
while(k && !q.empty()){
int u=q.top();
q.pop();
if(u>k){
cout<<ans+k<<endl;
return 0;
}
k-=u;
ans+=u+1;
}
cout<<ans+k<<endl;
return 0;
}
C
非常套路的DP题,没啥好说的(原本看错题了,-1)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2005,P=998244353;
int A(int x,int y){
x+=y;
if(x>=P) x-=P;
return x;
}
int n,m,f[N][N],g[N][N],C[N][N];
char a[N][N];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++){
scanf("%s",a[i]+1);
for(int j=1;j<=m;j++){
if(i==1 || j==1) C[i][j]=1;
else C[i][j]=A(C[i-1][j],C[i][j-1]);
if(i){
f[i][j]=f[i-1][j];
g[i][j]=1ll*g[i-1][j];
if(a[i][j]=='Y' && a[i-1][j]=='Y'){
g[i][j]=A( g[i][j],C[i-1][j] );
f[i][j]=A(f[i][j],A(C[i-1][j],2*g[i-1][j]%P));
}
}
if(j){
f[i][j]=A(f[i][j],f[i][j-1]);
g[i][j]=A(g[i][j],g[i][j-1]);
if(a[i][j]=='Y' && a[i][j-1]=='Y'){
g[i][j]=A( g[i][j],C[i][j-1] );
f[i][j]=A(f[i][j],A(C[i][j-1],2*g[i][j-1]%P));
}
}
//cout<<i<<" "<<j<<" "<<C[i][j]<<" "<<g[i][j]<<" "<<f[i][j]<<endl;
}
}
cout<<f[n][m]<<endl;
return 0;
}
D
先考虑一维怎么做:就每两个Y分组,看它们之间有几个空格可以选,把每个空格数乘起来就是方案数。
考虑二维,发现分成的格子数是确定的,而切分的方式又是横平竖直的,那么直接枚举因数,可以算出对应行和列分别切成的段数。那先考虑其中一维怎么切,发现是相似的,每块都是2k个Y;而对于第一维不同的切法,第二维对应的问题都一样,即每块都是2个Y,两维的切法实际是独立的,得到一维的合法方案,用来算下一位,暴力算的基础上优化一下即可(+4)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2005,P=998244353;
int n,m,s[N][N],k;
char a[N][N];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf(" %c",&a[i][j]);
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
if(a[i][j]=='Y') s[i][j]++,k++;
}
}
if(k&1){
puts("0");
return 0;
}
k>>=1;
int ans=0;
for(int a=1;a<=k;a++) if(!(k%a)){
int c[N*N]={0},d[N*N]={0},ft[N*N]={0};
int b=k/a;
//cout<<"a="<<a<<" b="<<b<<endl;
int lt=1;
for(int i=1;i<n;i++){
for(int j=lt;j<a;j++) if(s[i][m]==2*j*b){
c[j]++;
if(!ft[j]) ft[j]=i;
lt=j;
break;
}
}
ft[a]=n; c[a]=1;
int su=1;
for(int i=1;i<=a;i++) su=1ll*su*c[i]%P;
if(!su) continue;
lt=1;
for(int i=1;i<m;i++){
bool pd=0;
for(int j=lt;j<=lt+1;j++){
bool p=0;
for(int u=1;u<=a;u++) if(s[ft[u]][i]-s[ft[u-1]][i]!=j*2){
p=1;
break;
}
if(!p){
if(lt>j+1){
pd=1;
break;
}
lt=j,d[j]++;
break;
}
}
if(pd) break;
}
d[b]=1;
for(int i=1;i<=b;i++) su=1ll*su*d[i]%P;
(ans+=su)%=P;
}
cout<<ans<<endl;
return 0;
}
浙公网安备 33010602011771号