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

posted @ 2023-03-02 21:57  sz[sz]  阅读(54)  评论(0)    收藏  举报