其他分享
首页 > 其他分享> > 构造

构造

作者:互联网

构造题常常需要发现一些隐蔽的性质。提高观察力!

CF1670E

题目大意

给定一个 \(n = 2^p\) 个节点的树的形态,需要给每个点和每条边赋权值,一共 \(n\) 个点和 \(n-1\) 条边,权值在\([1, 2n-1]\) 里面选并且不能重复。然后钦定一个根节点。

需要使根节点到每个点和每条边的路径上的权值异或和的最大值最小\(^{*}\),求构造方案。

\(^{*}\) 比如下图,\(p=2\),根节点是权值为 \(3\) 的点:
image
所有参与比较的异或和为:
\(3 ;\)
\(3⊕7=4;\)
\(3⊕7⊕6=2;\)
\(3⊕2=1;\)
\(3⊕2⊕1=0;\)
\(3⊕2⊕1⊕4=4;\)
\(3⊕2⊕1⊕4⊕5=1.\)
最大值为 \(4\),是该形态树的一个最优解。

题目分析

观察性质,可以看出这个最小的最大值(简记为答案)应该是 \(n = 2^p\):

由于存在 \(2^p \sim 2^{p + 1}\) 的权值并且每个权值至少贡献一次,所以当第一次碰到这类权值的时候,异或和的第 \(p+1\) 位是 \(1\),因此答案不会小于 \(2^p\)。

考虑如何构造一颗满足答案为 \(n\) 的树。

我们有如下做法:

为什么此法可行?因为每一条链上的异或和都是形如
\(n\)
\(n ⊕ n+k_1 = k_1 < n\)
\(n ⊕ n+k_1 ⊕ k_1 = 0\)
\(n ⊕ n+k_1 ⊕ k_1 ⊕ k_2 = k_2 < n\)
\(n ⊕ n+k_1 ⊕ k_1 ⊕ k_2 ⊕ n+k_2 = n\)
\(n ⊕ n+k_1 ⊕ k_1 ⊕ k_2 ⊕ n+k_2 ⊕ n+k_3 = n ⊕ n+k_3 = k_3 < n\)
\(...\)
这样的东西,满足要求。

个人代码

#include<bits/stdc++.h>
using namespace std;
#define f(i, a, b) for(int i = a; i <= b; i++)
#define mod9 998244353
#define mod1 1000000007
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
#define endl '\n'
vector<vector<int>> g;
int cnt, n;
int node[300010], ver[300010];
map<pii, int> m;
void dfs(int x, int fa) {
    f(i, 0, g[x].size() - 1) {
        if(g[x][i] != fa) {
            int nxt = g[x][i];
            if(node[x] >= n) {
                int v = m[make_pair(x, nxt)];
                ver[v] = n + cnt; node[nxt] = cnt;
                cnt++;
            }
            else {
                int v = m[make_pair(x, nxt)];
                ver[v] = cnt; node[nxt] = n + cnt;     
                cnt++;       
            }
            dfs(nxt, x);
        }
    }
}
 
int main(){
    ios::sync_with_stdio(0);
    cin.tie(NULL);
    cout.tie(NULL);
    int t; cin >> t;
    while(t--) {
        int p; cin >> p; n = (1 << p);
        g.clear(); g.resize(n + 5);
        f(i, 1, n - 1) {
            int u, v; cin >> u >> v; g[u].push_back(v); g[v].push_back(u); 
            m[make_pair(u, v)] = i; m[make_pair(v, u)] = i;
        }
        cnt = 1;
        cout << 1 << endl;
        node[1] = n;
        dfs(1, 0);
        f(i, 1, n) cout << node[i] << " \n"[i == n];
        f(i, 1, n - 1) cout << ver[i] << " \n"[i == n - 1];
    }
    
    return 0;
}

标签:nxt,cnt,int,构造,权值,pair,节点
来源: https://www.cnblogs.com/Zeardoe/p/16637791.html