其他分享
首页 > 其他分享> > CF1605D - Treelabeling——二分图思维题

CF1605D - Treelabeling——二分图思维题

作者:互联网

掉分。

用语:
"MSB":二进制下的位数

第一步(果断钦定)

很显然的是,编号MSB不同的两个点之间的边就是断的。
一件重要的事情就是果断地钦定必然存在一种方案使得所有边都是断的。
这样一来,从每个点先手都可以赢,一定最优。

第二步(如何构造)

又是一个难点。
一个重要的思维过程是,每条边都限制两端的点的MSB不同,而相互之间没有连边的点则没有这条限制,那么让所有MSB相同的点之间都不连边,那么二部树(树的二分图)就能满足这一点——我们只需要对树进行二部染色,然后让MSB相同的点统统位于同一点集内即可。

第三步(如何划分)

一定能保证对于每一个MSB,MSB等于它的点一定能划分到同一点集内吗?没错。也就是说每一个 MSB 都可以被一个点集包含满
不妨设二部树中有 \(w\) 个白点,\(b\) 个黑点,不妨令 \(w\le b\)。又因为 \(w+b=n\),所以 \(w\le \frac{n}{2}\)。
如果能够证明白点点集能够把它内部的点的MSB包含满,并且知道哪些点要被划分到白点点集内,那么剩下的就一定划分到黑点点集内并且上述命题成立。下面讲述白点点集的构造方法。
我们知道这 \(1\sim n\) 一定由 \(2^0\) 个 MSB=1、\(2^1\) 个MSB=2,\(2^2\) 个MSB=3……的点构成,最后一个 MSB 不一定完整。那么易得,对于 \(k\) 最大的 \(2^0+2^1+...+2^k\le n\),\(2^0+2^1+...+2^k\ge \frac{n}{2}\),那么 \(w\le 2^0+2^1+...+2^k\),这就等价于 \(w\) 可以用 \(\{2^0,2^1,...,2^k\}\) 中的二的幂次进行二进制唯一分解:\(w=2^{p_1}+2^{p_2}+...+2^{p_q}(p_{i-1}<p_i\le k)\),那么我们知道了 \(p_1,...,p_q\) 自然也就知道完整地选择哪一些 MSB 可以使得白点点集恰好包含满这些MSB。
要选的点分别是 \([2^{p_1},2^{p_1+1}),[2^{p_2},2^{p_2+1}),...,[2^{p_q},2^{p_q+1})\)。剩下的就是黑点点集中的点。

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,c[N],d[N],p[N];
vector<int>G[N];
void ran(int x,int col){
	c[x]=col;
	for(int i=0;i<G[x].size();i++){
		int y=G[x][i];
		if(c[y]<0)ran(y,col^1);
	}
}
void solve(){
	cin>>n;
	for(int i=0;i<=n;i++)c[i]=-1;
	for(int i=0;i<=n;i++)G[i].clear();
	for(int i=1,u,v;i<n;i++)cin>>u>>v,G[u].push_back(v),G[v].push_back(u);
	ran(1,1);
	int cnt=0,_c=1;
	for(int i=1;i<=n;i++)cnt+=c[i];
	if(n-cnt<cnt)_c=0,cnt=n-cnt;
	for(int i=1;i<=n;i++)d[i]=_c^1;
	for(int i=0;i<=19;i++)
		if((cnt>>i)&1)
			for(int j=1<<i;j<(1<<(i+1));j++)d[j]=_c;
	for(int i=1,j=1;i<=n;i++)if(c[i]==_c){
		while(d[j]!=_c)j++;
		p[i]=j++;
	}
	for(int i=1,j=1;i<=n;i++)if(c[i]!=_c){
		while(d[j]==_c)j++;
		p[i]=j++;
	}
	for(int i=1;i<=n;i++)cout<<p[i]<<' ';puts("");
}
int main(){int t;cin>>t;while(t--)solve();}

标签:二分,Treelabeling,le,CF1605D,int,点点,集内,+...+,MSB
来源: https://www.cnblogs.com/impyl/p/15551387.html