Codeforces Round #529 (Div. 3) CDEF题解

C. Powers Of Two

题意:给你一个数n,你要找出是否存在k个 2^i 这样的数,使得这k个数的和是n。

思路:水题,如果n换成二进制1的个数小于k,那么可以把高一位的数换成2*低一位的数,比如1000换成两个100即可。

#include<bits/stdc++.h>
using namespace std;
map<int,int>mp;
map<int,int>::iterator it;
int ss(int x)
{
	int res=0,n=1;
	while(x)
	{
		if(x&1)
		{
			mp[n]=1;
			res++;
		}
		n=n*2;
		x/=2;
	}
	return res;
}
int main()
{
	int n,k;
	cin>>n>>k;
	if(k>n||ss(n)>k)puts("NO");
	else
	{
		puts("YES");
		int res=ss(n);
		for(int i=30;i>0;i--)
		{
			if(res==k)break;
			int x=(int)pow(2,i);
			int y=x/2;
			int t=mp[x];
			if(!t)continue;
			if(k-res>t)
			mp[x]=0,mp[y]+=2*t,res+=t;
			else
			{
				int cha=k-res;
				mp[x]-=cha;
				mp[y]+=2*cha;
				res=k;
			}
		}
		for(it=mp.begin();it!=mp.end();it++)
		{
			int x=it->first;
			int y=it->second;
			for(int i=1;i<=y;i++)
			printf("%d ",x);
		}
	}
}

D. Circular Dance

题意:有n个人连接一个单向环,给出第 i 个人的下一个人和下下个人的编号(有可能是反的),求出这n个人构成的环。

思路:可以直接从1开始推,如果1的后面有3和5,那么再分别看3和5,如果3的后面有5,1的后面可能是3,如果5的后面有3,1的后面就可能是5。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+11;
int p[maxn],a[maxn][2],vis[maxn];
int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	scanf("%d%d",&a[i][0],&a[i][1]);
	int i=1,res=1;
	printf("1");
	vis[1]=1;
	while(res<n)
	{
		int x=a[i][0],y=a[i][1];
		if((a[x][0]==y||a[x][1]==y)&&!vis[x])
		p[i]=x;
		else
		p[i]=y;
		i=p[i];
		vis[i]=1;
		printf(" %d",i);
		res++;
	}
}

E. Almost Regular Bracket Sequence

题意:给你一个长度为n的括号串,你可以独立的选择一个位置,改变那个位置的括号之后,如果总括号串是平衡的,这就是一个合法位置,求一共有多少个合法的位置。

思路:先撸一个线段树解法吧,设两颗线段树tl,tr分别表示区间 ')' 的个数和区间'('个数。那么怎么进行区间合并呢,假设左儿子为:))(((,右儿子为:))(((,合并消除()之后就变成了))((((,所以我可以从1枚举到n,在线段树中改变第 i 个位置的括号,如果线段树根节点的tl=tr=0,那么答案就++。

线段树:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
char s[maxn];
int tl[maxn*4],tr[maxn*4];
void push(int o,int ls,int rs)
{
	if(tr[ls]>=tl[rs])
	{
		tl[o]=tl[ls];
		tr[o]=tr[rs]+tr[ls]-tl[rs];
	}
	else
	{
		tr[o]=tr[rs];
		tl[o]=tl[ls]+tl[rs]-tr[ls];
	}
}
void build(int o,int l,int r)
{
	if(l==r)
	{
		if(s[l]=='(')
		tr[o]=1;
		else tl[o]=1;
		return;
	}
	int m=(l+r)/2,ls=o*2,rs=o*2+1;
	build(ls,l,m);
	build(rs,m+1,r);
	push(o,ls,rs);
}
void up(int o,int l,int r,int k)
{
	if(l==r)
	{
		tl[o]=!tl[o];
		tr[o]=!tr[o];
		return;
	}
	int m=(l+r)/2,ls=o*2,rs=o*2+1;
	if(k<=m)up(ls,l,m,k);
	else up(rs,m+1,r,k);
	push(o,ls,rs);
}
int main()
{
	int n,ans=0;
	cin>>n;
	scanf("%s",s+1);
	build(1,1,n);
	for(int i=1;i<=n;i++)
	{
		up(1,1,n,i);
		if(tl[1]==0&&tr[1]==0)ans++;
		up(1,1,n,i); 
	}
	cout<<ans;
}

o(n)代码:前缀后缀搞一搞就行。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e6+10;
char s[maxn];
int a[maxn],mn[maxn],n,a2[maxn],mn2[maxn],ans;
int main()
{
	cin>>n;
	scanf("%s",s+1);
	for(int i=1;i<=n;i++)
	{
		if(s[i]=='(')a[i]=a[i-1]+1;
		else a[i]=a[i-1]-1;
		mn[i]=min(mn[i-1],a[i]);
	}
	for(int i=n;i;i--)
	{
		if(s[i]==')')a2[i]=a2[i+1]+1;
		else a2[i]=a2[i+1]-1;
		mn2[i]=min(mn2[i+1],a2[i]);
	}
	if(a[n]!=2&&a[n]!=-2)
	{
		cout<<0;
		return 0;
	}
	for(int i=1;i<=n;i++)
	if(s[i]=='('&&a[n]==2)
	{
		if(mn[i-1]>=0&&mn2[i+1]>=0&&a[i-1]>=1)
		ans++;
	}
	else if(s[i]==')'&&a[n]==-2)
	{
		if(mn[i-1]>=0&&mn2[i+1]>=0&&a2[i+1]>=1)
		ans++;
	}
	cout<<ans;
}

F. Make It Connected

题意:给你m条带权值的边,和n个有权值的点,你可以使用这些边,也可以不使用而直接连接x  y,权值为ax+ay,求最小生成树。

思路:找出最小的ap,然后加n-1条 p 到 i 的权值为ap+ai的边,再加上那m条边,然后再用kruskal算法求最小生成树即可。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+10;
ll v[maxn],mn=1e15;
int n,m,p,res,par[maxn];
struct node
{
	int x,y;
	ll w;
	bool operator<(const node&t)const
	{ 
		return w<t.w;
	}
}a[maxn*2];
int find(int x)
{
	if(par[x]!=x)
	par[x]=find(par[x]);
	return par[x];
}
ll kruskal()
{
	sort(a+1,a+1+m);
	ll ans=0;
	for(int i=1;i<=m;i++)
	{
		int fx=find(a[i].x),fy=find(a[i].y);
		if(fx==fy)continue;
		ans+=a[i].w;
		par[fx]=fy;
		if(++res==n-1)return ans;
	}
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		par[i]=i;
		scanf("%lld",&v[i]);
		if(mn>v[i])
		mn=v[i],p=i;
	}
	for(int i=1;i<=m;i++)
	scanf("%d%d%lld",&a[i].x,&a[i].y,&a[i].w);
	for(int i=1;i<=n;i++)
	{
		if(i==p)continue;
		++m;
		a[m].x=p,a[m].y=i,a[m].w=v[p]+v[i];
	}
	cout<<kruskal();
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长沙橘子猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值