ABC425A-E题解

比赛链接

A题

这道题需要你求立方数的和,但是有些数需要加,有些数需要减,我们发现-1的n次方只为-1或0,并且是交替的,所以我们直接用一个变量反复交替,然后开for循环维护就行.

#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,ans;
bool f;
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		if(!f) ans-=i*i*i;
		else ans+=i*i*i;
		f=!f;
	}cout<<ans;
	return 0;
}

B题

这道题给你一个不完整的序列,询问你有没有一个1~n的排列可以满足这个序列的要求.对这道题首先我们应该判断是否有重复元素,有的话肯定不行,否则我们每次随意找一个没有的数填上去就行.

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=15;
int n,a[N],t[N];
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		if(a[i]!=-1) t[a[i]]++;
	}
	for(int i=1;i<=n;i++){
		if(t[a[i]]>1){
			cout<<"No";
			return 0;
		}
	}cout<<"Yes"<<endl;
	for(int i=1;i<=n;i++){
		if(a[i]!=-1) cout<<a[i]<<' ';
		else{
			for(int j=1;j<=n;j++){
				if(t[j]) continue;
				else{
					cout<<j<<' ';
					t[j]=1;
					break;
				}
			}
		}
	}
	return 0;
}

C题

这是一道RMQ问题,有两个操作,一个是查询区间和,一个是执行k次将前面的元素移到末尾.那么我们其实只需要维护一个前缀和以及一个最前面的元素的下标即可.我们通过这两个值可以分情况,如果查询区间不会跨过n我们直接求就行,否则我们可以把区间分成两段依次求.

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10;
int n,q,a[N],op,l,r,s[N],h=1,ans;
signed main(){
	cin>>n>>q;
	for(int i=1;i<=n;i++) cin>>a[i], s[i]=s[i-1]+a[i];
	while(q--){
		cin>>op>>l;
		if(op==1){
			l%=n,h+=l;
			if(h>n) h-=n;
		}else{
			cin>>r;
			l=l+h-1,r=r+h-1;
			if(l>n) l-=n;
			if(r>n) r-=n;
			if(r<l) ans=s[n]-s[l-1]+s[r];
			else ans=abs(s[r]-s[l-1]);
			cout<<ans<<endl;
		}
	}
	return 0;
}

D题

这一道题让你模拟一个操作.操作给的非常抽象,用朴素的人话说就是想象有一个白色格子,如果只有一个和它四联通的黑色格子那么就把它染成黑色,然后一直这样直到无法入队.

这里一开始我想到的是用朴素BFS,用队列维护黑色格子,但这个方法是错的.这里存图方法有问题,大家看一眼思路就行.

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+10;
int n,m,sl,cnt;
char a[N];
int fx[10]={0,0,1,0,-1};
int fy[10]={0,1,0,-1,0};
int t(int x, int y){
	return (x-1)*n+y;
}
struct node{
	int x,y;
};
bool check(int x, int y){
	if(x<1||x>n||y<1||y>m) return 0;
	if(a[t(x,y)]=='#') return 0;
	int tx,ty;
	cnt=0;
	for(int i=1;i<=4;i++){
		tx=x+fx[i], ty=y+fy[i];
		if(tx<1||tx>n||ty<1||ty>m) continue;
		if(a[t(tx,ty)]=='#') cnt++;
	}
	if(cnt==1) return 1;
	else return 0;
}
void bfs(){
	queue<node>q;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(a[t(i,j)]=='#'){
				q.push({i,j}), sl++;
			}
		}
	}
	while(q.size()){
		node t1=q.front();
		q.pop();
		int tx,ty;
		for(int i=1;i<=4;i++){
			tx=t1.x+fx[i], ty=t1.y+fy[i];
			if(check(tx,ty)){
				q.push({tx,ty}), sl++, a[t(tx,ty)]='#';
			}
		}
	}
}
signed main(){
	cin>>n>>m;
	for(int i=1;i<=n*m;i++) cin>>a[i];
	bfs(), cout<<sl<<endl;
//	for(int i=1;i<=n;i++){
//		for(int j=1;j<=m;j++){
//			cout<<a[t(i,j)];
//		}cout<<endl;
//	}
	return 0;
}

为什么这是错的?我们考虑这种情况:已经有一个黑色格子找到了一个白色格子,这时还没有其他黑色格子更新那么这个白色格子就被更新了.然而,这个操作其实是同时进行的,也就是说有可能同时更新的还有这个白色格子附近的其他黑色格子,这就会导致这个本不该入队的格子入队了.

那么我们应该如何修改呢?我们这次只维护可以入队的白色节点,这些格子之后都会变黑,因此不会影响结果.我们再维护一个队列,这个队列维护的是可能入队的白色节点,在最后我们把这些节点统计一下,筛出可以的加入原来的队列.如果没有就结束,这样代码会长一点.

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+10;
int n,m,sl;
char x;
int fx[10]={0,0,1,0,-1};
int fy[10]={0,1,0,-1,0};
struct node{
	int x,y;
};
vector<char>a[N];
vector<bool>vis[N];
vector<int>cnt[N];
void bfs(){
	queue<node>q,w;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(a[i][j]=='#') continue;
			int tx,ty;
			for(int d=1;d<=4;d++){
				tx=i+fx[d], ty=j+fy[d];
				if(tx<1||tx>n||ty<1||ty>m) continue;
				if(a[tx][ty]=='#') cnt[i][j]++;
			}
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(cnt[i][j]==1){
				vis[i][j]=1, q.push({i,j});
			}
		}
	}
	do{
		while(q.size()){
			node t=q.front();
			q.pop();
			int tx,ty;
			a[t.x][t.y]='#';
			for(int i=1;i<=4;i++){
				tx=t.x+fx[i], ty=t.y+fy[i];
				if(tx<1||tx>n||ty<1||ty>m||a[tx][ty]=='#') continue;
				cnt[tx][ty]++, w.push({tx,ty});
			}
		}
		while(w.size()){
			node t=w.front();
			w.pop();
			if(vis[t.x][t.y]) continue;
			if(cnt[t.x][t.y]==1) q.push({t.x,t.y});
		}
	}while(q.size());
}
signed main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		a[i].push_back('.'), cnt[i].push_back(0), vis[i].push_back(0);
		for(int j=1;j<=m;j++){
			cin>>x, a[i].push_back(x);
			cnt[i].push_back(0), vis[i].push_back(0);
		}
	}
	bfs();
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(a[i][j]=='#') sl++;
		}
	}
	cout<<sl;
	return 0;
}

E题

这道题是一道组合数学题.我们抛开如何计算组合数,单从数学方面入手可以分别看每个数字.我们可以把一个数字的出现次数看成在几个下标中选出几个,因此要用组合数.我们可以先求出所有数量,然后算完一个数字减去他出现的次数即可.

那么我们应该如何算组合数呢?因为最多是5000,所以用n^2的杨辉三角可以算出,然后就可以O(1)的算出组合数.

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+10,M=5e3+10;
int t,m,c[M][M],n,a[N],cnt,sum;
signed main(){
	cin>>t>>m, c[0][0]=1;
	for(int i=1;i<=5000;i++){
		c[i][0]=1;
		for(int j=1;j<=5000;j++) c[i][j]=(c[i-1][j-1]+c[i-1][j])%m;
	}
	while(t--){
		cin>>n, cnt=1;
		for(int i=1;i<=n;i++) cin>>a[i], sum+=a[i];
		for(int i=1;i<=n;i++) cnt*=c[sum][a[i]], sum-=a[i], cnt%=m;
		cout<<cnt%m<<'\n';
	}
	return 0;
}

难度建议

红红黄黄(或绿?)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值