CF1605D题解
cyber_coder · · 题解
题目大意:
E 和 S 在玩博弈游戏, E 先手 S 后手。
给一个
E 先随便选择一个点,占领它(点 u ), S 只能选择与这个点相邻的,没有被占领的点(点 v )且这两个点满足
后者就是解题的关键
先不必管第一种,这个题是构造,只要你能构造出来最大情况,有些东西是不必考虑的。
现在让我们看看神奇的异或操作
当两个数(u,v)的二进制最高位数相等时,它俩的异或结果一定比
因为最高位在异或之后变成了 0
相反,如果两个数的二进制最高位不相等,异或结果永远比最小的那个大,因为最高位的二进制位被保留。
我们就可以把
(1) ; (2,3) ; (4,5,6,7) ; (8,9,10,11,12,13,14,15) ; ……
不同的划分块之间不通(不满足约束条件)
树我们用bfs把它变成二分图,任意一边的节点数量小于等于
举个例子:假设
3 的二进制是 11 ,所以我们选择第一个划分块和第二个划分块(划分块的大小和二进制权重是相对应的,所以划分块里面的元素刚刚好被用完)来填充二分图的左边,剩下的自然就填充到右边。
此时你会发现,你无论选择哪个点,对手死路一条。
上代码:
#include<bits/stdc++.h>
using namespace std;
int t,n;
vector<int>edge[200005];
bool vis[200005];
int ans[200005];
vector<int>Left,Right;//二部图
void bfs(int i,int depth)//奇数在左边,偶数在右边
{
vis[i]=1;
if(depth&1) Left.push_back(i);
else Right.push_back(i);
for(int j=0; j<(int)edge[i].size(); j++)
{
if(!vis[ edge[i][j] ])
{
bfs(edge[i][j],depth+1);
}
}
}
int main()
{
cin >> t;
while(t--)
{
cin >> n;
for(int i=1; i<=n; i++) edge[i].clear();
Left.clear();
Right.clear();
memset(vis,0,n+2);
for(int i=1; i<n; i++)
{
int u,v;
cin >> u >> v;
edge[u].push_back(v);
edge[v].push_back(u);
}
bfs(1,1);
memset(vis,0,n+2);
int s=Left.size();
int S=Right.size();
int base=1;
if(s<S)
{
while(s)
{
if(s&1)
{
for(int i= (1 << (base-1)); i<= (1 << base) -1; i++)
{
ans[Left.back()]=i;
Left.pop_back();
vis[i]=1;
}
}
base ++;
s >>= 1;
}
for(int i=1; i<=n; i++)
{
if(!vis[i])
{
ans[Right.back()]=i;
Right.pop_back();
}
}
}
else
{
while(S)
{
if(S&1)
{
for(int i= (1 << (base-1)); i<= (1 << base) -1; i++)
{
ans[Right.back()]=i;
Right.pop_back();
vis[i]=1;
}
}
base ++;
S >>= 1;
}
for(int i=1; i<=n; i++)
{
if(!vis[i])
{
ans[Left.back()]=i;
Left.pop_back();
}
}
}
for(int i=1; i<=n; i++) cout << ans[i] << " " ;
cout << endl;
}
return 0;
}