【Codeforces】Round #525 (Div. 2) C-F

17 篇文章 0 订阅

传送门:cf1088


C.Ehab and a 2-operation task

n n n次加+1次取模,满足最后 a i = i a_i=i ai=i
每个数最少需要加的次数是 i − a i ( m o d n + 1 ) i-a_i\pmod {n+1} iai(modn+1),由于要加的值必须单调不增,所以倒着处理每个数需要加的值 n d i nd_i ndi,若 n d i &lt; n d i + 1 nd_i&lt;nd_{i+1} ndi<ndi+1,就 n d i + = ( n + 1 ) nd_i+=(n+1) ndi+=(n+1)。最后模 n + 1 n+1 n+1即可。

#include<bits/stdc++.h>
using namespace std;
const int N=2005;

int n,a[N],nd[N],bs=0;

int op[N],u[N],v[N],cnt;

int main(){
	int i,j;
	scanf("%d",&n);
	for(i=1;i<=n;++i) scanf("%d",&a[i]);
	for(i=n;i;--i){
	   nd[i]=bs+i-a[i];
	   for(;nd[i]<0 || nd[i]<nd[i+1];){
	   	  bs+=(n+1);nd[i]+=(n+1);
	   }
	}
	for(i=n;i;--i){
		if(nd[i]>nd[i+1]){
			op[++cnt]=1;u[cnt]=i;
			v[cnt]=nd[i]-nd[i+1];	
		}
	}
	op[++cnt]=2;u[cnt]=n;v[cnt]=n+1;
	printf("%d\n",cnt);
	for(i=1;i<=cnt;++i) printf("%d %d %d\n",op[i],u[i],v[i]);
	return 0;
}

D.Ehab and another another xor problem

倒着从大到小逐位考虑,得到高位的准确值后,相当于每次都在询问最高位。

可以对于第 i i i位,可以分别考虑 a , b a,b a,b这一位上为 ( 1 , 1 ) , ( 0 , 0 ) , ( 1 , 0 ) . ( 0 , 1 ) (1,1),(0,0),(1,0).(0,1) (1,1),(0,0),(1,0).(0,1)的情况。

但注意光考虑 b c bc bc这一位为 ( 1 , 1 ) , ( 0 , 0 ) , ( 1 , 0 ) . ( 0 , 1 ) (1,1),(0,0),(1,0).(0,1) (1,1),(0,0),(1,0).(0,1)四种情况中的任意两种都是无法推出唯一的 a , b a,b a,b确定值的(可以自己写一下),还需要知道 a , b a,b a,b去掉最高位后的大小。

但每位最多询问2次,而这2次中必然有一次包含了 a , b a,b a,b去掉最高位后的大小关系。

于是可以先记录 a , b a,b a,b的大小关系,逐步递推去掉最高位后的大小关系,答案就是唯一的了。

#include<bits/stdc++.h>
using namespace std;
const int N=2005;

int n,a,b,v,mw;

int main(){
	int i,j,x,y;
	cout<<"? "<<a<<" "<<b<<endl;
	scanf("%d",&mw);
	for(i=29;~i;--i){
		j=(1<<i);
		cout<<"? "<<a<<" "<<(b|j)<<endl;
		scanf("%d",&x);
		cout<<"? "<<(a|j)<<" "<<(b|j)<<endl;
		scanf("%d",&y);
		if((!x)){
			if(y==-1) a|=j;
			else b|=j; 
			mw=0;
		}else if((!y)){
			if(x==1) a|=j,b|=j;
			mw=0;
		}else if((x==1)&&(y==-1)){
			if(mw==1) a|=j;
			else a|=j,b|=j,mw=-1;
		}else if((x==-1)&&(y==1)){
			if(mw==-1) b|=j; 
		}else if((x==1)&&(y==1)){
			if(mw==-1) mw=1,b|=j;
			else mw=1,a|=j,b|=j;
		}else{
			if(mw==1) mw=-1,a|=j;
			else mw=-1;
		}
	}
	cout<<"! "<<a<<" "<<b<<endl;	
	return 0;
}

E.Ehab and a component choosing problem

首先不考虑 k k k,只贪心让答案值尽量大: d p i dp_{i} dpi表示 i i i所在的联通块最大值,一遍 d f s dfs dfs求得最大值 a n s = m a x ( d p i ) ans=max(dp_i) ans=max(dpi)

注意到再把这个最大的连通块分成多块,或者取其它值更小的联通块,答案显然不是最优的。

那么再贪心取出最多的值为 a n s ans ans的不相交连通块即可(贪心向下取)。

std:

#include <iostream>
#include <vector>
using namespace std;
int a[300005],k;
long long dp[300005],ans=-1e9;
vector<int> v[300005];
void dfs(int node,int p,bool f)
{
	dp[node]=a[node];
	for (int u:v[node])
	{
		if (u!=p)
		{
			dfs(u,node,f);
			dp[node]+=max(dp[u],0LL);
		}
	}
	if (f)
	ans=max(ans,dp[node]);
	else if (dp[node]==ans)
	{
		dp[node]=0;
		k++;
	}
}
int main()
{
	int n;
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	scanf("%d",&a[i]);
	for (int i=1;i<n;i++)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		v[a].push_back(b);
		v[b].push_back(a);
	}
	dfs(1,0,1);
	dfs(1,0,0);
	printf("%I64d %d",ans*k,k);
}

F. Ehab and a weird weight formula

将点对边的贡献转成点对 w w w的贡献:

类似于MST的做法,考虑每次加入一个点 i i i(连向以选中集合中的一点 j j j),贡献为 a i + a j ( 1 + ⌈ log ⁡ 2 d i s ( i , j ) ⌉ ) a_i+a_j(1+\lceil\log_2dis(i,j)\rceil) ai+aj(1+log2dis(i,j))。称这条边为 i i i的所属边(值最小的点没有所属边)。最小化 a j ( 1 + ⌈ log ⁡ 2 d i s ( i , j ) ⌉ ) a_j(1+\lceil\log_2dis(i,j)\rceil) aj(1+log2dis(i,j))即可。

那么构造出的这颗树必然满足父亲结点的值 ≤ \leq 儿子结点的值。

于是以值最小的点为根向下 d f s dfs dfs,维护每个点 2 k 2^k 2k级祖先,存在这样一个性质:每个点的所属边必然只可能连向它的某个 2 k 2^k 2k级祖先或者根节点。
因为若它的所属边不连向根,而 a r o o t &lt; a i ( i ≠ r o o t ) a_root&lt;a_i(i\neq root) aroot<ai(i̸=root),则它必然连向了更近的点,且这个点在 ⌈ log ⁡ 2 d i s ( i , j ) ⌉ \lceil\log_2dis(i,j)\rceil log2dis(i,j)相等的情况下要尽量小(深度尽量小)。

复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

std:

#include <iostream>
#include <string.h>
#include <vector>
using namespace std;
vector<int> v[500005];
int m=1,a[500005],dp[20][500005];
long long ans;
void dfs(int node,int p)
{
	dp[0][node]=p;
	for (int i=1;i<20;i++)
	{
		if (dp[i-1][node]!=-1)
		dp[i][node]=dp[i-1][dp[i-1][node]];
	}
	int d;
	long long mn=(1LL<<60);
	for (d=0;d<20 && dp[d][node]!=-1;d++)
	mn=min(mn,(long long)(d+1)*a[dp[d][node]]+a[node]);
	mn=min(mn,(long long)(d+1)*a[m]+a[node]);
	if (p!=-1)
	ans+=mn;
	for (int u:v[node])
	{
		if (u!=p)
		dfs(u,node);
	}
}
int main()
{
	int n;
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		if (a[i]<a[m])
		m=i;
	}
	for (int i=1;i<n;i++)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		v[a].push_back(b);
		v[b].push_back(a);
	}
	memset(dp,-1,sizeof(dp));
	dfs(m,-1);
	printf("%I64d",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值