Codeforces Round #702 (Div. 3) ABCDE

这篇博客探讨了算法竞赛中的几种常见问题及其解决策略。第一题关注于数组操作,通过位运算优化求解;第二题涉及数组平衡,通过两次循环确保每个模3余数相同;第三题建立立方数表,判断数是否可拆分为两个立方和;第四题利用线段树寻找区间最大值确定树结构;最后一题模拟代币对决,确定赢家。这些题目展示了在不同场景下如何运用数据结构和算法解决问题。
摘要由CSDN通过智能技术生成

比赛链接

TA

在这里插入图片描述
题意:问要插入最少多少个数让整个数组满足题目的条件,即相邻两个数之间大小补相差二倍
idea:遍历一遍,对每两个相邻的数,看看较小的数*2多少次后可以大于等于较大的数,利用位运算方便点

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define LL long long
#define N 555
#define INF 0x3f3f3f3f
using namespace std;

long long ans = 0;
int n,t,a[N],last,maxx,minn;

int main()
{
	cin>>t;
	while(t--)
	{
		ans = 0;
		scanf("%d",&n);
		scanf("%d",&a[1]);
		for(int i=2;i<=n;i++)
		{
			scanf("%d",&a[i]);
			if( a[i]<a[i-1] )
			{
				minn = a[i];
				maxx = a[i-1];
			}
			else
			{
				minn = a[i-1];
				maxx = a[i];
			}
			while( (minn<<1) < maxx )
			{
				ans++;
				minn<<=1;
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}

TB

在这里插入图片描述
题意:数组有n个数,n是3的倍数,你一次可以让一个数加一,问最少多少步操作后可以使得数组内各个数对3取模后得到的余数的个数一样,即平均。

idea:先统计,然后对比余数为0,1,2的数与n/3的大小关系,2不够从1补,1不够从0补,0不够从2补,循环两遍(颇有锻环成链的感觉)就可了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define LL long long
#define N 40000
#define INF 0x3f3f3f3f
using namespace std;

int t,n,m,ans,a[5],s;

void work()
{
	if( a[2]<m )
	{
		ans += (m-a[2]);
		a[1] -= (m-a[2]);
		a[2] = m;
	}
	if( a[1]<m )
	{
		ans += (m-a[1]);
		a[0] -= (m-a[1]);
		a[1] = m;
	}
	if( a[0]<m )
	{
		ans += (m-a[0]);
		a[2] -= (m-a[0]);
		a[0] = m;
	}
}

int main()
{
//	freopen("in.txt","r",stdin);
	cin>>t;
	while(t--)
	{
		ans = 0;
		a[1] = 0;
		a[2] = 0;
		a[0] = 0;
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&s);
			a[ s%3 ]++;
		}
		m = n/3;
		work();
		work();
		printf("%d\n",ans);
	}
	return 0;
}

TC

在这里插入图片描述
题意:问每个数能不能拆成两个数的立方的和

idea:看一眼数据范围最大不过10000的立方,考虑先打一个1到10000的立方的表,然后对每一个数n,再枚举一遍其中一个数的立方和,判断相减得到的另一个数是否为立方数即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#include<queue>
#define LL long long
#define N 40000
#define INF 0x3f3f3f3f
using namespace std;

map<LL,int> q;
LL t,n,m,a[10100],tag;
void FI()
{
	for(int i=1;i<=10001;i++)
	{
		a[i] = i*i;
		a[i] *= i;
		q[ a[i] ] = 1;
	}
}

int main()
{
//	freopen("in.txt","r",stdin);
	cin>>t;
	FI();
	while(t--)
	{
		bool pd = false;
		scanf("%lld",&n);
		for(int i=10001;i>=1;i--)
		{
			if( a[i]<=n )
			{
				tag = i;
				break;
			}
		}
		for(int i=tag;i>=1&&pd==false;i--)
		{
			if( q.count( n-a[i] ) > 0 )
			{
				pd = true;
				break;
			}
		}
		if( pd ) printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}

TD

在这里插入图片描述

题意:给出一数列,要求将区间内的最大值作为数的根节点,最大值左右部分分别作为左右子树建树,求每个数所对应的节点所在的深度。

idea:线段树查找区间最大值

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define LL long long
#define N 200
#define INF 0x3f3f3f3f
using namespace std;

int T,n,a[N],sum = 0,ans[N],tag,maxx,xu[N];
struct node
{
	int l,r,val;
}t[N*4];

void build(int p,int l,int r)
{
	t[p].l = l,t[p].r = r;
	int ls = p<<1,rs = (p<<1)|1,mid = (l+r)>>1;
	if( l!=r )
	{
		build(ls,l,mid);
		build(rs,mid+1,r);
		t[p].val = max( t[ls].val , t[rs].val );
	}
	else t[p].val = a[l];
}

int check(int p,int l,int r)
{
	int ls = p<<1,rs = p<<1|1,mid = (t[p].l+t[p].r)>>1;
	if( l==t[p].l && r==t[p].r ) return t[p].val;
	if( r<=mid ) return check(ls,l,r);
	else if( l>mid ) return check(rs,l,r);
	else return max( check(ls,l,mid) , check(rs,mid+1,r) );
}

void work(int p,int h,int l,int r)
{
	int pos;
	ans[p] = h;
	if( l==r ) return ;
	if( l<p )
	{
		pos = check(1,l,p-1);
		pos = xu[ pos ];
		work(pos,h+1,l,p-1);
	}
	if( r>p )
	{
		pos = check(1,p+1,r);
		pos = xu[ pos ];
		work(pos,h+1,p+1,r);
	}
	return ;
}

int main()
{
//	freopen("in.txt","r",stdin);
	cin>>T;
	while(T--)
	{
		memset(xu,0,sizeof(xu));
		memset(ans,0,sizeof(ans));
		memset(t,0,sizeof(t));
		maxx = -1;
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			xu[ a[i] ] = i;
			if( a[i]>maxx )
			{
				tag = i;
				maxx = a[i];
			}
		}
		build(1,1,n);
		work(tag,0,1,n);
		for(int i=1;i<=n;i++) printf("%d ",ans[i]);
		cout<<endl;
	}
	return 0;
}

TE

在这里插入图片描述
题意:给出若干个人的初始代币数,随机挑选2个代币数不为0的人对决,代币数多的一方获胜并拿走败方的所有代币(相同时随机一个胜者),最后剩下代币数不为0的一人赢得比赛,求有机会赢得比赛的人的编号。

idea:先排序。首先,如果一个人能赢,那么金币数大于他的也一定能赢。其次,一个人可以赢下所有金币数比他小的,因此我们可以求一个前缀和,只要他能赢下比他大的第一个人且后一个人能赢,当前这个人一定能赢。我们只要找第一个不能赢的人就好啦,比他大的一定能赢,比他小的一定赢不了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#include<queue>
#define LL long long
#define N 40000
#define INF 0x3f3f3f3f
using namespace std;

int sum,ans[201000],t,n,tag;
LL q[201000];
struct node
{
	int val,xu;
}e[200100];
bool cmp(node x,node y)
{
	return x.val<y.val;
}
void work()
{
	for(int i=n-1;i>=1;i--)
	{
		if( q[i] < e[i+1].val-e[i].val )
		{
			tag = i;
			break;
		}
	}
	sum = n-tag;
	for(int i=1;i<=sum;i++)
	{
		ans[i] = e[n-i+1].xu;
	}
	sort(ans+1,ans+sum+1);
}

int main()
{
	cin>>t;
	while(t)
	{
		scanf("%d",&n);
		for(int i=1;i<=n;i++) scanf("%d",&e[i].val);
		sort(e+1,e+n+1,cmp);
		for(int i=1;i<=n;i++) q[i] = q[i-1] + e[i].val;
		work();
		cout<<sum<<endl;
		for(int i=1;i<=sum;i++)
		cout<<ans[i]<<" ";
		cout<<endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值