[AGC052B] Tree Edges XOR 做题笔记

Erlija Gäte
神仙题

题意

有一个拥有奇数个节点的带边权树。可以进行无限次如下的操作:

  • 选择一条边权为 \(w_i\) 的边,然后让所有和这条边相邻的边的边权都异或上 \(w_i\)。(两条边相邻,当且仅当这两条边恰好又一个公共点)
    每一个边有一个期望目标 \(t_i\),问能否在操作后让所有边的边权都等于这条边的期望目标。
    \(n\leq 10^5, w_i, t_i\leq 10^7\)

解法

边权的变化十分复杂,不具有良好的性质。考虑将一个用一个新的特征信息去刻画边权信息,要求能够建立边权信息确定的状态到特征信息确定的状态的单射,并且边权的变化所对应的特征信息的变化具有好的性质。

为了让一次边权操作对特征信息的改动尽量小,可以尝试将特征信息建立在点上。以期达到只有 \(u, v\) 这两个点的特征信息变化的情况,为了达到这一点,考虑到两次异或同一个数会抵消的性质,显然特征值回是树上的一段路径边权异或和。一个想法是钦定一个根后,一个点的点权为这个点到根的路径的异或和。容易发现如果对于一条不连接根的边执行操作,其效果相当于交换这两个点的点权。

但是如果对一条连接根的边执行操作,情况为比较复杂。为此可以选择一个度数为 \(1\) 的点作为根,那么这样的边就只有一条。

如果全程不选择这条边,那么问题转化为有长度为 \(n-1\) 的序列,可以任意打乱顺序,问能否做到和另一个长度为 \(n-1\) 的序列相等。这样的话直接把序列变为(可重)集合,判断集合是否相等即可。

考虑如果选中这一条边(设它连接了 \(rt\)\(u\),边权为 \(w\),所以 \(u\) 的点权也就是 \(w\))做操作,效果是让集合中除了 \(w\) 之外的所有数都异或上 \(w\)

又考虑到可以通过交换,把任何一个点权换到 \(u\) 的位置上,所以问题转化为:

有一个大小为 \(n-1\) 的(可重)集合 \(S\),执行任意次操作,每次选择 \(a\in S\),然后令 \(S\leftarrow \{x\oplus a|x\in S, x\ne a\}\cup\{a\}\)。问能否变成另外一个集合 \(T\)

注意一个重要性质:\(S\) 经过两次不重复操作能变成的集合都能通过一次操作代替(你可以模拟一下对两个不同的数依次执行操作的效果,它和一次操作完全一样),所以实际上只需要执行一次操作就足够了。

因为每次操作会改变 \(n-2\) 个数,即会改变奇数个数,因此 \(\bigoplus_{x\in S}x\) 会异或上 \(a\)。这样就可以计算出 \(a\)。如果 \(a\notin S\),则无解,否则模拟一下操作,然后判断集合是否相等。

时间复杂度 \(O(n\log n)\)

评析

关键是要想到利用特征值的变化来反映复杂的,难以刻画的变化,比如本题中点权刻画边权。这算是一个经典技巧。

那如何想到设置点权为到根的路径边权异或和?其实这类特征值的设计方案不多,直接枚举即可。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i, a, b) for(ll i=a;i<=b;i++)
#define rrep(i, a, b) for(ll i=a;i>=b;i--)
const ll N=1e5+9;
ll n;
vector <ll> to[N], len1[N], len2[N];
ll d1[N], d2[N];
void dfs(ll u, ll fa){
    for(ll i=0;i<to[u].size();i++){
        ll v=to[u][i];
        if(v==fa)continue;
        d1[v]=d1[u]^len1[u][i];
        d2[v]=d2[u]^len2[u][i];
        dfs(v, u);
    }
}
int main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin >> n;
    rep(i, 1, n-1){
        ll u, v, w1, w2;
        cin >>u >> v >> w1 >> w2;
        to[u].emplace_back(v);to[v].emplace_back(u);
        len1[u].emplace_back(w1);len2[u].emplace_back(w2);
        len1[v].emplace_back(w1);len2[v].emplace_back(w2);
    }
    ll rt=0;
    rep(i, 1, n)if(to[i].size()==1){
        rt=i;break;
    }
    dfs(rt, 0);
    ll w=0;
    rep(i, 1, n){
        w^=d1[i]^d2[i];
    }
    rep(i, 1, n){
        if(d1[i]==w){
            rep(j, 1, n)if(j!=i&&j!=rt){
                d1[j]^=w;
            }
            sort(d1+1, d1+n+1);sort(d2+1, d2+n+1);
            rep(i, 1, n)if(d1[i]!=d2[i]){cout << "No" << endl;return 0;}
            cout << "Yes" << endl;return 0;
        }
    }
    cout << "No" << endl;
    return 0;
}

posted @ 2025-06-23 21:33  yanzihe  阅读(9)  评论(0)    收藏  举报