Codeforces Round #498 (div3) C++

文章介绍了五道C++编程题目,涉及数组操作(如奇偶数替换、数组分块与区间划分)、排序算法(结构体排序)、军事问题(先序遍历与节点计数)以及异或路径计算。展示了如何使用C++实现这些算法和数据结构问题的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A. Adjacent Replacements

题意:对于每对数(1和2,3和4,...),替换之后留下的都是小的那个数,而小数都是奇数,所以从前往后处理,遇到偶数减一,奇数不变

C++代码如下:

#include<iostream>
using namespace std;
const int N=1010;
int a[N];
int n;
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		int x;
		cin>>x;
		if(x%2)a[i]=x;
		else a[i]=x-1;
	}
	for(int i=1;i<=n;i++)cout<<a[i]<<' ';
	return 0;
}

B. Polycarp's Practice

题意:将数组分成k部分,要求每部分的最大值加起来最大,把每个数及其下标放在结构体中,对结构体按数进行从大到小排序,找出前k大的数,把他们的下标进行排序,按照下标找区间长度

C++代码如下:

#include<iostream>     
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
struct Node{
	int x;
	int id;
};
bool cmp1(Node a,Node b){
	if(a.x!=b.x)return a.x>b.x;
	return a.id<b.id;
}
Node st[2000+5];
int a[2000+5];
int b[2000+5];
int main(){
	int n,k;
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		cin>>st[i].x;
		st[i].id=i;
	}
	sort(st+1,st+1+n,cmp1);
	int sum=0;//存储前k大的数 
	for(int i=1;i<=k;i++){
		sum=sum+st[i].x;
		a[i]=st[i].id;//前k大的数的下标 
	}
	sort(a+1,a+1+k);//下标排序 
	for(int i=1;i<=k;i++){//根据下标进行求划分区间(关键在于第一个区间和最后一个区间) 
		if(i==1){
			b[i]=a[i];
			if(a[2]!=0)b[i]=b[i]+a[2]-a[1]-1;//重点 
			if(a[2]==0)b[i]=n; 
		}else if(1<i&&i<k){
			b[i]=a[i+1]-a[i];
		}else b[i]=n-a[i]+1;
	}
	cout<<sum<<endl;
	for(int i=1;i<=k;i++){
		if(i<k)printf("%d ",b[i]);
		else printf("%d\n",b[i]);
	}
	return 0;
} 

C. Three Parts of the Array

题意:将一个数组分成三块(可能有某块为空),能使左右两块相等的最大的相等值,每一块的值等于里面所有数的和,维护前缀和s[]和后缀和last[],用双指针寻找左右两块相等的最大值输出即可

C++代码如下:

#include<iostream>
using namespace std;
const int N=200010;
int a[N];
long long s[N],last[N];
int n;
int main(){
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++)s[i]=s[i-1]+a[i];
	for(int i=n;i>=1;i--)last[i]=last[i+1]+a[i];
	int i=0,j=n+1;
	long long maxx=0;
	while(i<j){
		if(s[i]==last[j])maxx=s[i],i++,j--;
		else if(s[i]<last[j])i++;
		else j--;
	}
	cout<<maxx<<endl;
	return 0;
}

D. Two Strings Swaps

题意:问字符串a要至少预交换几个字母才可以和b通过题目的三个操作使两个字符串相等

注意:只能换a中的字母

C++代码如下:

#include<iostream>
using namespace std;
int main(){
	int n;
	cin>>n;
	string a,b;
	cin>>a>>b;
	int sum=0;
	for(int i=0;i<(a.size()+1)/2;i++){
		char a1,a2,b1,b2;
		a1=a[i],a2=a[n-i-1],b1=b[i],b2=b[n-i-1];
		if(i!=n-i-1){
            //可以由三个操作使a,b相等
			if(a1==a2&&b1==b2)continue;
			if(a1==b1&&a2==b2)continue;
			if(a1==b2&&a2==b1)continue;
            //不能由三个操作使a,b相等
			if(b1==b2||a2==b1||a1==b2||a2==b2||a1==b1)sum++;
			else sum+=2;
		}else if(a1!=b1)sum++;
	}
	cout<<sum<<endl;
	return 0;
}

E. Military Problem

题意:遍历是先序遍历,用一个数组存储先序遍历的结果,记录每个节点在先序遍历中出现的下标,预处理以每个节点为根的子树中节点的数量

C++代码如下:

#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
const int N=200010;
int h[N],e[N],ne[N],idx;
int s[N];
vector<int> v;
bool st[N];
int cnt[N];//cnt[i]记录以i为根的子树中的节点数量 
int id[N];//id[i]保存每个节点在遍历时出现的下标
int n;
void add(int a,int b){
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int dfs(int u){
	v.push_back(u);
	id[u]=v.size()-1;
	st[u]=true;//标记当前点已经搜过
	int sum=1;//sum记录以u为根节点的子树中的节点数量
	for(int i=h[u];i!=-1;i=ne[i]){//遍历所有的子节点
		int j=e[i];
		if(!st[j])dfs(j);//如果没搜过,则搜
		sum+=cnt[j];//加上cnt[j]
	}
	cnt[u]=sum;//保存以u为根节点的子树中的节点数量
	return sum;//返回
}
int main(){
	int n,q;
	cin>>n>>q;
	memset(h,-1,sizeof h);
	for(int i=2;i<=n;i++)cin>>s[i];
    //因为要从小到大遍历,所以建树的时候从大到小插入
	for(int i=n;i>=2;i--)add(s[i],i);
	dfs(1);//从根节点开始深搜,预处理所有的节点
	while(q--){
		int u,k;
		cin>>u>>k;
        //如果以u为根的子树中的节点数量小于k,则直接输出-1
		if(cnt[u]<k)cout<<-1<<endl;
		else cout<<v[id[u]+k-1]<<endl;
	}
	return 0;
}

F. Xor-Paths

题意:找到从左上角走到右下角的所有数的异或值等于k的所有方案,输出个数

C++暴力代码如下(会超时,但思路简单清晰):

#include<iostream>
using namespace std;
const int N=25;
typedef long long LL;
LL g[N][N];
LL n,m,ans,k;
void dfs(int u,int v,LL sum){
	if(u==n&&v==m){
		if(sum==k)ans++;
		return;
	}
	//往右走
	if(v+1<=m)dfs(u,v+1,sum^g[u][v+1]); 
	//往下走
	if(u+1<=n)dfs(u+1,v,sum^g[u+1][v]); 
}
int main(){
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			cin>>g[i][j];
	dfs(1,1,g[1][1]);
	cout<<ans<<endl;
	return 0;
}

C++正解:写两个搜索函数,一个从头开始搜,一个从尾开始搜,每个都搜一半,用第二个函数搜索的时候进行判断

异或的性质:若a^b=c,则a^c=b,b^c=a

代码如下:

#include<iostream>
#include<map>
using namespace std;
const int N=22;
map<long long,int>v[N][N];
int n,m;
int half;
long long k;
long long a[N][N];
long long ans;
void dfs1(int x,int y,long long sum,int cnt){
	sum^=a[x][y];
	if(cnt==half){
		v[x][y][sum]++;
		return;
	}
	if(x+1<n)dfs1(x+1,y,sum,cnt+1);
	if(y+1<m)dfs1(x,y+1,sum,cnt+1);
}
void dfs2(int x,int y,long long sum,int cnt){
	if(cnt==n+m-2-half){
		if(v[x][y].count(k^sum))
			ans+=v[x][y][k^sum];
		return;
	}
	if(x>0)dfs2(x-1,y,sum^a[x][y],cnt+1);
	if(y>0)dfs2(x,y-1,sum^a[x][y],cnt+1);
}
int main(){
	cin>>n>>m>>k;
	half=(n+m-2)/2;//记录一半是多少 
	for(int i=0;i<n;i++){
		for(int j=0;j<m;j++){
			cin>>a[i][j];
		}
	}
	dfs1(0,0,0,0);
	dfs2(n-1,m-1,0,0);
	cout<<ans<<endl;
	return 0;
}

题目描述 给定一棵 $n$ 个节点的树,每个节点都有一个权值 $w_i$。你需要删去树上的一些边,使得剩下的每个连通块中,所有节点的权值之和不超过 $k$。 求最多能删去多少条边。 输入格式 第一行包含两个整数 $n,k$。 第二行包含 $n$ 个整数 $w_1,w_2,\cdots,w_n$。 接下来 $n-1$ 行,每行包含两个整数 $a,b$,表示节点 $a$ 和节点 $b$ 之间有一条边。 输出格式 输出一个整数,表示最多能删去的边数。 数据范围 $1\le n \le 10^5$ $1 \le k,w_i \le 10^9$ 输入样例1: 5 6 2 3 1 5 4 1 2 1 3 2 4 2 5 输出样例1: 2 输入样例2: 5 3 2 3 1 5 4 1 2 1 3 2 4 2 5 输出样例2: 0 算法1 (dfs) $O(n)$ 首先我们可以想到暴力的做法,即对于每个连通块,暴力枚举删去哪些边,尝试得到最多的删边数。那么如何求解一个连通块内的所有节点权值之和呢?我们可以使用 DFS 遍历树,对于每个节点,求出其子树内所有节点的权值之和,那么以该节点为根的子树内的所有节点权值之和就是该节点自身的权值加上其所有子节点的权值之和。 对于每个连通块,我们枚举该连通块内任意两个节点 $x,y$。如果 $x$ 与 $y$ 之间的路径上的所有边都被删去了,那么 $x$ 和 $y$ 就会分别成为两个新的连通块,这两个新的连通块内所有节点的权值之和都不超过 $k$。因此我们只需要枚举所有的 $x,y$ 对,对于每个 $x,y$ 对尝试删去它们之间的路径上的所有边,看是否能够让 $x$ 和 $y$ 成为两个新的连通块,进而更新答案即可。 时间复杂度 参考文献 python3 代码 算法2 (暴力枚举) $O(n^2)$ blablabla 时间复杂度 参考文献 C++ 代码 class Solution { public: const int N = 1e5+10; int n,k; int h[N],e[N<<1],ne[N<<1],idx; int w[N]; int sum[N]; bool st[N]; int res; void add(int a,int b) { e[idx] = b,ne[idx] = h[a],h[a] = idx++; } void dfs(int u,int father) { sum[u] = w[u]; for(int i=h[u];~i;i=ne[i]) { int j = e[i]; if(j == father) continue; dfs(j,u); sum[u] += sum[j]; } } void dfs2(int u,int father) { for(int i=h[u];~i;i=ne[i]) { int j = e[i]; if(j == father) continue; dfs2(j,u); if(sum[j] <= k) res++; else if(sum[u] - sum[j] <= k) res++; } } void solve() { cin >> n >> k; memset(h,-1,sizeof(h)); for(int i=1;i<=n;i++) cin >> w[i]; for(int i=1;i<n;i++) { int a,b; cin >> a >> b; add(a,b); add(b,a); } dfs(1,-1); dfs2(1,-1); cout << res << endl; } }; int main() { Solution().solve(); return 0; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值