Codeforces Round 788 (Div. 2) E. Hemose on the Tree(树上构造)

题目

t(t<=5e4)组样例,每次给定一个数p,

表示一棵节点数为n=2^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]是对称的两半

于是,有了申老师的构造:

Codeforces Round 788 (Div. 2) E. Hemose on the Tree(树上构造)_第1张图片

这个构造是不对的,不过稍微改一下就对了

注意到n\bigoplus (n+3) \bigoplus 3\bigoplus (n+6)=n+6

所以,应该交换6和n+6的顺序,

也就是异或值为n时,边用n+c,点用c

异或值为0时,边用c,点用n+c

这启发我们记一下当前层数的奇偶,然后搜索下去即可

根显然可以任意选取一个,赋上值为n

代码

#include
//#include
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 P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<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<

你可能感兴趣的:(构造,树,树,构造)