题目
t(t<=5e4)组样例,每次给定一个数p,
表示一棵节点数为的树,
以下n-1条边,读入树边
对于n个点和n-1条边,每个点需要赋权,每条边需要赋权,
权值需要恰好构成[1,2n-1]的排列
并且当你赋完权之后,你需要选择一个点当根,
对于一端为根,另一端为一个点或一条边的任意路径,
要求路径上的权值异或和(路径上的每条边的边权和每个点的点权都要参与异或)的最大值最小,
输出这个最小值
保证sumn不超过3e5
思路来源
申老师
题解
首先,答案不会小于n,因为n是2的幂次,占了一个二进制位
如果答案小于n,意味着任意一端为根的路径,n这一位都出现了偶数次,
但是至少有一个点会有n这一位,意味着会从偶数次变成奇数次,所以显然不成立
那么,考虑答案能不能是n,考虑将根填成n,剩下的值域[1,n-1]和[n+1,2n-1]是对称的两半
于是,有了申老师的构造:
这个构造是不对的,不过稍微改一下就对了
注意到
所以,应该交换6和n+6的顺序,
也就是异或值为n时,边用n+c,点用c
异或值为0时,边用c,点用n+c
这启发我们记一下当前层数的奇偶,然后搜索下去即可
根显然可以任意选取一个,赋上值为n
代码
#include<bits/stdc++.h>
//#include<iostream>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<int,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define scll(a) scanf("%lld",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
const int N=(1<<17)+5;
int t,p,n,u[N],v[N],a[N],b[N],dep[N],c;
vector<P>e[N];
void dfs(int u,int fa,int w){
a[u]=w;
for(auto &x:e[u]){
int v=x.fi,id=x.se;
if(v==fa)continue;
c++;
dep[v]=dep[u]+1;
if(dep[v]&1){
b[id]=n+c;
dfs(v,u,c);
}
else{
b[id]=c;
dfs(v,u,n+c);
}
}
}
void sol(){
sci(p);
n=(1<<p);
rep(i,1,n)e[i].clear();
c=0;
rep(i,1,n-1){
sci(u[i]),sci(v[i]);
e[u[i]].pb(P(v[i],i));
e[v[i]].pb(P(u[i],i));
}
dfs(1,0,n);
puts("1");
rep(i,1,n)printf("%d%c",a[i]," \n"[i==n]);
rep(i,1,n-1)printf("%d%c",b[i]," \n"[i==n-1]);
}
int main(){
sci(t); // t=1
while(t--){
sol();
}
return 0;
}