D. Treelabeling
-
题意:给你一颗\(n\)个节点的树,让你给每个点赋不同值\([1,n]\),博弈,先手先选起点\(u\),之后每次选相邻的点\(v\),同时满足\(u \oplus v\le min(u,v)\).问你如何赋值,使得先手选择并且能赢的起点最多.
-
题解:先看\(u\oplus v\le min(u,v)\)这个条件,很简单,只有当两个数最高位同位才成立,那么也就自然能得一个想法,我们让相邻的点最高位不同,也就是二分图黑白染色,那么先手无论选哪个点都是必赢,不难发现,对于每个最高位的数有:\(2^0,2^1,2^2,....,n-2^k+1\)个,对于某一种颜色的个数,我们一定可以用最高位不同的数的个数凑出来(想象成二进制,一定是可以的).也就是实现麻烦点,我代码写的比较丑qwq。
-
代码:
#include <bits/stdc++.h> #define ll long long #define fi first #define se second #define pb push_back #define me memset #define rep(a,b,c) for(int a=b;a<=c;++a) #define per(a,b,c) for(int a=b;a>=c;--a) const int N = 1e6 + 10; const int mod = 1e9 + 7; const int INF = 0x3f3f3f3f; using namespace std; typedef pair<int,int> PII; typedef pair<ll,ll> PLL; ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;} ll lcm(ll a,ll b) {return a/gcd(a,b)*b;} vector<int> edge[N]; int color[N]; int lg[N]; int ans[N]; int vis[N]; void lg_init(){ for(int i=1;i<=200000;++i){ int k=0; while(1<<(k+1)<=i) k++; lg[i]=k; } } void dfs(int u,int fa,int c){ color[u]=c; for(auto to:edge[u]){ if(to==fa) continue; dfs(to,u,c^1); } } int main() { lg_init(); int _; scanf("%d",&_); while(_--){ int n; scanf("%d",&n); unordered_map<int,int> mp; for(int i=1;i<n;++i){ int u,v; scanf("%d %d",&u,&v); edge[u].pb(v); edge[v].pb(u); } for(int i=1;i<=n;++i){ mp[lg[i]]++; } dfs(1,-1,0); int cnt0=0; vector<int> v; for(int i=1;i<=n;++i) if(color[i]==0) cnt0++; for(int i=lg[n];i>=0;--i){ if(cnt0>=mp[i]){ cnt0-=mp[i]; int now=pow(2,i); for(int j=0;j<mp[i];++j){ v.pb(now+j); vis[now+j]=true; } } } int p=0; vector<int> res; for(int i=1;i<=n;++i){ if(color[i]==0) ans[i]=v[p++]; if(!vis[i]) res.pb(i); } p=0; for(int i=1;i<=n;++i){ if(color[i]==1) ans[i]=res[p++]; } for(int i=1;i<=n;++i) printf("%d ",ans[i]); puts(""); for(int i=1;i<=n;++i){ vis[i]=false; } for(int i=1;i<=n;++i) edge[i].clear(); mp.clear(); v.clear(); } return 0; }