Codeforces Round #756 (Div. 3)

这篇博客详细解析了Codeforces Round #756 (Div. 3)的比赛题目,包括A. Make Even、B. Team Composition、C. Polycarp Recovers the Permutation等,涉及数论、图论和动态规划等多个知识点,适合ACM竞赛选手和编程爱好者学习。

A. Make Even

题意:

​ 给你一个数字,你可以任意反转他的前缀,问最少反转几次可以使得这个数为偶数

分析:

​ 首先很明显的是,如果给的数中每一位都是奇数那么很明显不可能

  • 刚开始就是偶数答案为0
  • 最高位为偶数答案为1
  • 其次答案为2

代码:

#include<bits/stdc++.h>
using namespace std;

int main()
{
	ios::sync_with_stdio(0);
	int T;cin>>T;
	while (T--)
	{
		int num;cin>>num;
		if (num%2==0)
		{
			cout<<"0\n";
			continue;
		}vector<int> ns;
		int cnt=0;
		while (num)
		{
			ns.push_back(num%10);
			if (num%10%2==0)++cnt;
			num/=10;
		}
		if (cnt==0)
		{
			cout<<"-1\n";
			continue;
		}
		if (ns.back()%2==0)
		{
			cout<<"1\n";
			continue;
		}
		cout<<"2\n";
	}
}

B. Team Composition: Programmers and Mathematicians

题意:

​ 有aaa个数学家,bbb个工程师,要求组最多的444人队伍,要求每个队伍至少有一名数学家和一名工程师

分析:

​ 如果a=ba=ba=b,那么我们肯定两个数学家两个工程师这样组队

如果不相等的话,那么我们肯定先让人多派三个人,人少的派一个人。

代码:

#include<bits/stdc++.h>
using namespace std;

int main()
{
	ios::sync_with_stdio(0);
	int T;cin>>T;
	while (T--)
	{
		int n,m;cin>>n>>m;
		if (n<m)swap(n,m);
		int cnt = min({n/3,m,(n-m)/2});
		n -= 3*cnt;
		m -= cnt;
		cout<<cnt+min(n/2,m/2)<<"\n";
	}
}

C. Polycarp Recovers the Permutation

题意:

​ 一个长为nnn的排列,每次对比两端数字的大小,取小的数字

  • 小的数字是左端的,则放入新排列的左端
  • 小的数字是右端的,则放入新排列的右端

如此,构成了一个新排列

现在给你一个新排列,要求你构造出老排列

分析:

​ 很明显的一个结论是,最大的那个数nnn一定在新排列的最左端过着最右端

因为,他是最大的,一定是最后被放入的

然后我们可以这样构造老排列,先在新排列找到最大的数nnn,然后翻转其他的数

就是老排列了

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+100;
int a[maxn];
int n;

int main()
{
	ios::sync_with_stdio(0);
	int T;cin>>T;
	while (T--)
	{
		cin>>n;
		for (int i=1;i<=n;++i)cin>>a[i];
		if (a[1]==n)reverse(a+2,a+1+n);
		else if (a[n]==n)reverse(a+1,a+n);
		else 
		{
			cout<<"-1\n";
			continue;
		}
		for (int i=1;i<=n;++i)cout<<a[i]<<" ";cout<<endl;
	}
}

D. Weights Assignment For Tree Edges

题意:

​ 给你一棵树,请你给边分配权值>0>0>0

要求,将所有点按照到达根节点的距离排序大的排列,要符合题目给的排列

分析:

​ 一个节点到达根节点的距离,一定是大于他的父亲节点到达根节点的距离

因此,所给排列ppp中,p[i]p[i]p[i]的父节点一定在iii之前出现过了

因此,我们可以暴力的给边赋值

具体是,遍历到p[i]p[i]p[i],然后查看他的父节点fa[p[i]]fa[p[i]]fa[p[i]]

  • fa[p[i]]fa[p[i]]fa[p[i]]未被遍历到,返回−1-11
  • p[i]p[i]p[i]到达他父亲节点的边长为max⁡(1,dist[p[i−1]]−dist[fa[p[i]]]+1)\max(1, dist[p[i-1]]-dist[fa[p[i]]] +1)max(1,dist[p[i1]]dist[fa[p[i]]]+1)

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5+100;
int b[maxn],p[maxn],vis[maxn];
ll dis[maxn],res[maxn];
int n;
void solve()
{
	cin>>n;
	for (int i=1;i<=n;++i)
	{
		cin>>b[i];
		vis[i]=1;
		if (i==b[i])vis[i]=0;
	}
	for (int i=1;i<=n;++i)cin>>p[i];
	if (b[p[1]]!=p[1])
	{
		cout<<"-1\n";
		return;
	}dis[p[1]]=res[p[1]]=0;
	for (int i=2;i<=n;++i)
	{
		if (vis[b[p[i]]])
		{
			cout<<"-1\n";
			return;
		}
		res[p[i]]=max(1LL, dis[p[i-1]] - dis[b[p[i]]]+1);
		dis[p[i]] = dis[b[p[i]]]+res[p[i]];
		vis[p[i]]=0;
	}
	for (int i=1;i<=n;++i)cout<<res[i]<<" ";cout<<endl;
}
int main()
{
	ios::sync_with_stdio(0);
	int T;cin>>T;
	while (T--)
	{
		solve();
	}
}

E1. Escape The Maze (easy version)

题意:

​ 给一棵树,VladVladVlad在根节点,他的目标是走到一处叶子节点

VladVladVladkkk个朋友站在其他的一些点上,目的是拦下VladVladVlad,他们每次只能走一下,

VladVladVlad先手,问VladVladVlad能否走到一处叶子节点

分析:

​ 从朋友角度,我们可以发现,因为树的特殊性,朋友一定是不断向父节点走的!

每到达一处父节点,那么就可以覆盖掉该父节点代表的子树下的所有叶子节点!

$Vlad $选择一处叶子节点为目标,朋友们不断向父节点走,如果能提前走到根到叶子节点唯一路径上,

那么就被成功阻止了。

而,组织了VladVladVlad的朋友,最远也只能走到自己原来节点深度的121\over 221

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+100;
vector<int> G[maxn];
int dep[maxn],fa[maxn][20];
int vis[maxn],x[maxn];
int n,k;
void dfs(int u,int p)
{
	fa[u][0]=p;dep[u]=dep[p]+1;
	for (int v:G[u])if (v!=p)
		dfs(v,u);
}
bool dfs2(int u)
{
	if (G[u].size()==1&&u!=1)return true;
	for (int v:G[u])if (v!=fa[u][0]&&!vis[v]&&dfs2(v))return true;
	return false;
}
void solve()
{
	cin>>n>>k;
	for(int i=1;i<=k;++i)cin>>x[i];
	for (int i=1;i<=n;++i)G[i].clear(),vis[i]=0;
	for (int i=1,u,v;i<n;++i)
	{
		cin>>u>>v;
		G[u].push_back(v);
		G[v].push_back(u);
	}dfs(1,0);
	for (int k=1;k<20;++k)
		for (int i=1;i<=n;++i)fa[i][k] = fa[fa[i][k-1]][k-1];
	for (int i=1;i<=k;++i)
	{
		int xi = x[i];
		for (int k = 19;k>=0;--k)
			if (dep[fa[xi][k]]*2>dep[x[i]])xi = fa[xi][k];
		vis[xi]=1;
	}
	if(dfs2(1))
	{
		cout<<"Yes\n";
	}else cout<<"No\n";
}
int main()
{
	ios::sync_with_stdio(0);
	int T;cin>>T;
	while (T--)
	{
		solve();
	}
}

E2. Escape The Maze (hard version)

题意:

​ 和E1E1E1一样,不同的是问最少需要保留多少朋友才能成功阻止$Vlad $

分析:

​ 基本思路和E1E1E1一样,每个朋友都尽可能地向上走,去阻止。

因此,我们在第二次dfsdfsdfs遍历时,只需要统计被几个朋友挡回来就可以了

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+100;
vector<int> G[maxn];
int dep[maxn],fa[maxn][20];
int vis[maxn],x[maxn];
int n,k;
void dfs(int u,int p)
{
	fa[u][0]=p;dep[u]=dep[p]+1;
	for (int v:G[u])if (v!=p)
		dfs(v,u);
}
int ans = 0;
bool dfs2(int u)
{
	if (vis[u])
	{
		++ans;
		return false;
	}
	if (G[u].size()==1&&u!=1)return true;
	for (int v:G[u])if (v!=fa[u][0])
		if (dfs2(v))return true;
	return false;
}
void solve()
{
	cin>>n>>k;
	for(int i=1;i<=k;++i)cin>>x[i];
	for (int i=1;i<=n;++i)G[i].clear(),vis[i]=0;
	for (int i=1,u,v;i<n;++i)
	{
		cin>>u>>v;
		G[u].push_back(v);
		G[v].push_back(u);
	}dfs(1,0);
	for (int k=1;k<20;++k)
		for (int i=1;i<=n;++i)fa[i][k] = fa[fa[i][k-1]][k-1];
	for (int i=1;i<=k;++i)
	{
		int xi = x[i];
		for (int k = 19;k>=0;--k)
			if (dep[fa[xi][k]]*2>dep[x[i]])xi = fa[xi][k];
		vis[xi]=1;
	}
	ans = 0;
	if(!dfs2(1))
	{
		cout<<ans<<"\n";
	}else cout<<"-1\n";
}
int main()
{
	ios::sync_with_stdio(0);
	int T;cin>>T;
	while (T--)
	{
		solve();
	}
}

F. ATM and Students

题意:

​ 求出一个最长连续子序列保证其中所有的前缀和不小于kkk

分析:

双指针对数组a求最大连续子序列和

对某一段(l,r)(l,r)(l,r)区间满足答案要求,则更新答案,跳右指针。

若跳完右指针不满足条件,则不断跳lll直到满足条件。

代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
int t,n,m,k,l,r,ansl,ansr,sum,s;
int a[200005];
void init(){
	ansl=ansr=-1,sum=0,l=r=1;
}
void update(){
	if(r-l>ansr-ansl) ansr=r,ansl=l;
}
void solve(){
	cin>>n>>s;
	for(int i=1;i<=n;i++)cin>>a[i];
	init();
	while(l<=n&&r<=n+1){
		if(sum+s>=0)update(),sum+=a[r++];
		else sum-=a[l++];
	}
	if(ansr==-1) cout<<-1<<endl;
	else cout<<ansl<<" "<<ansr-1<<endl;
}
signed main(){
	cin>>t;
	while(t--)
		solve();
}

int t,n,m,k,l,r,ansl,ansr,sum,s;
int a[200005];
void init(){
ansl=ansr=-1,sum=0,l=r=1;
}
void update(){
if(r-l>ansr-ansl) ansr=r,ansl=l;
}
void solve(){
cin>>n>>s;
for(int i=1;i<=n;i++)cin>>a[i];
init();
while(l<=n&&r<=n+1){
if(sum+s>=0)update(),sum+=a[r++];
else sum-=a[l++];
}
if(ansr==-1) cout<<-1<<endl;
else cout<<ansl<<" "<<ansr-1<<endl;
}
signed main(){
cin>>t;
while(t–)
solve();
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值