Codeforces Round 936 (Div. 2)

本文讨论了在编程竞赛中解决A、B两个涉及排序(找到中位数)和最大子段和的问题,以及未完成的C题——树切割问题,分析了未能解决C题的原因,包括对二分法的忽视和对DFS在树问题中的应用理解不足。
摘要由CSDN通过智能技术生成

下次千万别在csdn里直接编辑,没保存,全没了!!!

A. Median of an Arra

排个序,找到中位数,从中位数开始往后数有几个数和中位数相等

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e5+10;
int a[N];
int n;
void solve() {
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	sort(a+1,a+1+n);
	if(n%2==0){
		int x=n/2;
		int cnt=1;
		for(int i=x+1;i<=n;i++){
			if(a[i]==a[i-1]) cnt++;
			else break;
		}
		cout<<cnt<<endl;
	}
	else{
		int x=(n+1)/2;
		int cnt=1;
		for(int i=x+1;i<=n;i++){
			if(a[i]==a[i-1]) cnt++;
			else break;
		}
		cout<<cnt<<endl;
	}
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t=1;
    cin>>t;
	while(t--) {
		solve();
	}
	return 0;
}

B. Maximum Sum

求出最大子段和,记为sum,每次把sum与和最大的子段放在一起

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=2e5+10,mod=1e9+7;
int a[N];
int n,k;
int dp[N];
void solve() {
	cin>>n>>k;
	for(int i=1;i<=n;i++) cin>>a[i];
	dp[0]=0;
	for(int i=1;i<=n;i++){
		dp[i]=max(a[i],dp[i-1]+a[i]);
	}
	int sum=0;
	for(int i=1;i<=n;i++) sum=max(sum,dp[i]);
//	cout<<sum<<endl;
	int ans=sum%mod;
	for(int i=1;i<k;i++){
		sum=(sum+sum)%mod;
		ans=(ans+sum)%mod;
	}
	for(int i=1;i<=n;i++) ans=(ans+a[i])%mod;
	cout<<(ans+mod)%mod<<endl;
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t=1;
    cin>>t;
	while(t--) {
		solve();
	}
	return 0;
}

C. Tree Cutting

n个节点的树,刚好删除k条边,问剩余连通块大小最小的最大值

最小的最大值==>二分答案

对于二分出的连通块大小最小的最大值,dfs一遍,统计满足答案的情况下最多能分成多少个连通块,如果个数大于等于k+1,那么合法,取一堆合法中最大的那个

trick:

1.最小的最大值==>平均分或者二分答案

2.对于树,是否要dfs:如果树的具体形态不同,答案千差万别,那么肯定要dfs

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n,k;
map<int,vector<int>>e;
int cnt;
int dfs(int u,int fa,int target){
	int now=1;
	for(auto v:e[u]){
		if(v==fa) continue;
		now+=dfs(v,u,target);
	}
	if(now>=target){
		cnt++;
		return 0;
	}
	return now;
}
bool check(int x){
	cnt=0;
	dfs(1,-1,x);
	if(cnt>=k+1) return true;
	return false;
}
void solve() {
	cin>>n>>k;
	e.clear();
	for(int i=0;i<n-1;i++){
		int u,v;
		cin>>u>>v;
		e[u].push_back(v);
		e[v].push_back(u);
	}
	int l=1,r=n;
	while(l<r){
		int mid=(l+r+1)/2;
		if(check(mid)) l=mid;
		else r=mid-1;
	}
	cout<<l<<endl;
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t=1;
    cin>>t;
	while(t--) {
		solve();
	}
	return 0;
}

总结:
这一阶段的目标是出A,B,C,本次比赛只出了A,B

A,B的思路都是很快就有的,没有什么卡顿,20分钟就出了,完全没有问题

现分析一下没有做出C题的原因
1.看到最小的最大值,立马想到平均分,把二分答案这一条路pass了,导致方向偏了,以后需要两条路都尝试

2.之前做过几道树的题目是不需要dfs的,只用分析度数,于是往这个方向想了很久,方向偏了,具体判断是否需要dfs的方法:如果树的具体形态不同,答案千差万别,那么肯定要dfs

3.对于dfs的熟练度不够,特别是对于归(递归的归)的理解,一般是求子树的大小,子树的权值和,子树的异或和

方法是记住哪个地方是归(递归的归),看以下代码注释

int dfs(int u,int fa,int target){
	int now=1;
	for(auto v:e[u]){
		if(v==fa) continue;
		now+=dfs(v,u,target);//从u往下递v这个分支,然后归回来了
	}
    //从u往下递完所有的分支,然后归回来了
	if(now>=target){
		cnt++;
		return 0;//返回0表示以u为根的子树自成一个连通块
	}
	return now;
}

另外分析一下做题的节奏以及心态

掌握好做题节奏,就我目前所知,自认为最好的方法就是一边读题一边用自己的话在记事本上记录,然后思考的东西也一并记在记事本上,同时计时,一般想10到20分钟没有一点思路的话,那么就可以放弃这道题了

这样做有以下几个好处:1.心态比较平稳,不容易急 2.题目读的仔细,不容易读错题 3.做完题目的同时写完了题解

心态上:现场赛焦急是正常的,只要按照以上的步骤,按部就班,掌握好做题的节奏就没问题

如果遇到不会做的题目该怎么办(比如这场比赛的C题,不包括超出能力范围的题):

方向思考错了真的很难走出自己的思维定势,只能重新慢慢读题,抛掉之前的思路,重新慢慢想,也得看看运气,因为很难跳出自己之前的思维,可以适时放弃

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值