Codeforces Round #525 (Div. 2) CDEF题解

C. Ehab and a 2-operation task

题意:给一个长度为n的数列,你有两种操作 :1 i x 表示从a1到ai每个数都加x,2 i x  表示从a1到ai每个数都模等于x,你至多有n+1次操作,求怎么把原数列构造成一个严格递增的数列。

思路:我们先把所有数都加上一个足够大的数(远大于n),假设前面 i 个数我已经构造成 1 2 3.... i 的数列,又因为后面的数远大于n,所以我只要把前 i+1个数全部模a[ i+1 ]-(i+1),那么第i+1个数就变成了 i+1,而且这次取模操作不影响前面 i 个数。

#include<cstdio>
using namespace std;
int a[10000];
int main()
{
	int n,k=100000;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	scanf("%d",&a[i]),a[i]+=k;
	printf("%d\n",n+1);
	printf("1 %d %d\n",n,k);
	for(int i=1;i<=n;i++)
	printf("2 %d %d\n",i,a[i]-i);
}

D. Ehab and another another xor problem

题意:你要求两个数 a b,你可以向系统问一个问题: ? c d,系统返回1 表示 a^c>b^d,返回-1则表示小于,返回0表示等于,已知a和b小于 1<<30,要求在至多62次询问求出a和b的值。

思路:这个题相当有意思,我们用二进制来看待这两个数,假设我已经知道了a和b的第i+1位到29位,并把已知的信息全部异或0得到c,d,我怎么求a和b的第 i 位了,我先dfs问第i位 ? c d,如果返回1,那么a和b有这三种情况:(xxx1xxx,xxx0xxx),(xxx1xxx,xxx1xxx),(xxx0xxx,xxx0xxx),显然我需要再到判断函数去判断,判断函数我再问 ?  c|(1<<i)  d|(1<<i),如果返回-1,说明a,b是(xxx1xxx,xxx0xxx),然后进行下一次dfs查找第i-1位,如果是1,麻烦了有两种情况,还要问一次,那我再问一次 ?  c|(1<<i)  d,就可以判断a和b的第 i 位是都为1 还是都为0了,不过我为了查询第 i 位用了3次操作,3*30>62,看起来要gg,其实没关系,因为我已经知道了,a和b的第i-1位的具体情况了:(xxxx1xx,xxxx0xx),(xxxx1xx,xxxx1xx),(xxxx0xx,xxxx0xx),求第i-1位我可以省下一个步骤,求到最后的 c d的答案就是a和b,我只推导到这,剩下的就靠你来ac了。

#include<cstdio>
#include<iostream>
using namespace std;
int a=0,b=0,x,i=29;
void dfs2();
void dfs()
{
	if(i<0)return;
	printf("? %d %d\n",a,b);
	cin>>x;
	dfs2();
}
void dfs2()
{
	if(i<0)return;
	if(x==1)
	{
		printf("? %d %d\n",a|(1<<i),b|(1<<i));
		cin>>x;
		if(x==-1)
		{
			a|=(1<<i);
			i--;
			dfs();
		}
		else
		{
			printf("? %d %d\n",a|(1<<i),b);
			cin>>x;
			if(x==-1)
			a|=(1<<i),b|=(1<<i);
			i--;
			x=1;
			dfs2();
		}
	}
	else if(x==-1)
	{
		printf("? %d %d\n",a|(1<<i),b|(1<<i));
		cin>>x;
		if(x==1)
		{
			b|=(1<<i);
			i--;
			dfs();
		}
		else
		{
			printf("? %d %d\n",a|(1<<i),b);	
			cin>>x;
			if(x==-1)
			a|=(1<<i),b|=(1<<i);
			i--;
			x=-1;
			dfs2();
		}
	}
	else
	{
		printf("? %d %d\n",a|(1<<i),b);	
		cin>>x;
		if(x==-1)
		a|=(1<<i),b|=(1<<i);
		i--;
		dfs();		
	}
}
int main()
{
	dfs();
	printf("! %d %d\n",a,b);
}

E. Ehab and a component choosing problem

题意:给你一棵树,每个节点有权值,你可以选择k个集合(每个节点只能属于一个集合,且集合内的点必须是联通的),使得所有集合的总权值/k最大,在满足这个条件的前提下,k也要最大。

思路:这真是个水题,比D题不知道简单多少.....首先我知道只选择一个集合肯定可以求出最大的ans,那么我首先解决ans,再解决k,设d[ i ]为以 i 为根节点的子树所构成的集合的最大权值。显然d[ i ]=max(d[ i ],d[ i ]+d[ son ]),通过此方法求出ans后,第二次dfs找到有几个d[ i ]=ans就可以了。

#include<cstdio>
#include<vector>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn=3e5+10,inf=1e9;
typedef long long ll;
int a[maxn],n,k=0;
ll d[maxn],ans=-inf;
vector<int>G[maxn];
void dfs(int u,int fa)
{
	d[u]=a[u];
	for(int i=0;i<G[u].size();i++)
	{
		int v=G[u][i];
		if(v==fa)continue;
		dfs(v,u);
		d[u]=max(d[u],d[u]+d[v]);
	}
	ans=max(ans,d[u]);
}
void dfs2(int u,int fa)
{
	d[u]=a[u];
	for(int i=0;i<G[u].size();i++)
	{
		int v=G[u][i];
		if(v==fa)continue;
		dfs2(v,u);
		d[u]=max(d[u],d[u]+d[v]);
	}
	if(d[u]==ans)
	d[u]=-inf,k++;
}
int main()
{
	int u,v;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int i=1;i<n;i++)
	{
		scanf("%d%d",&u,&v);
		G[u].push_back(v);
		G[v].push_back(u);
	}
	dfs(1,-1);
	dfs2(1,-1);
	cout<<ans*k<<' '<<k;
}

F. Ehab and a weird weight formula

题意:给你n个点的权值ai,一棵树,定义dist(a,b)为这棵树上两点的距离,且除了唯一的一个权值最小的点,每个点都必有一个相邻点权值比其小,要你构造一颗新的树,规定每加一条边u<-->v,w就加au+av+log2( dist(u,v) )* min( au , av )。求出最小的w。

思路: wa007

#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=5e5+5;
typedef long long ll;
int f[maxn][20],a[maxn];
vector<int>G[maxn];
ll ans=0,inf=1e18;
void dfs(int u,int fa)
{
	f[u][0]=fa;
	for(int i=1;i<20;i++)
	f[u][i]=f[f[u][i-1]][i-1];
	ll res=inf;
	for(int i=0;i<20;i++)
	res=min(res,(i+1ll)*a[f[u][i]]+a[u]);
	if(fa!=u)
	ans+=res;
	for(int i=0;i<G[u].size();i++)
	if(G[u][i]!=fa)
	dfs(G[u][i],u);
}
int main()
{
	int n,u,v,rt=1;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		if(a[i]<a[rt])
		rt=i;
	}
	for(int i=1;i<n;i++)
	{
		scanf("%d%d",&u,&v);
		G[u].push_back(v);
		G[v].push_back(u);
	}
	dfs(rt,rt);
	printf("%I64d\n",ans);
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

长沙橘子猫

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

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

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

打赏作者

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

抵扣说明:

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

余额充值