题解:CF1592C Bakry and Partitioning

本蒟蒻的第一篇题解IoI,求管理大大审过IoI

原题链接

好啦,这道题是什么意思呢?
就是给我们一棵树,每个节点有一个权值 \(a_i\),我们要化身园丁,我们被允许剪掉 \(1\)~\(k-1\) 条边,由于我们有强迫症,所以我们需要使得剪掉边后形成的所有连通块中,每个连通块内节点权值的异或和都相等。问是否存在这样的裁剪方案(即是否可以治好强迫症)。

思路

我们看到异或就能想的什么呢?
对了!就是 \(x \oplus x = 0\)

又由于剪完后的每个连通块内节点权值的异或和都必须相等,所以我们就能想到最后一定只有两种情况!

设整棵树的异或和 \(S = a_1 \oplus a_2 \oplus \dots \oplus a_n\)。

  • 情况 1: \(S = 0\)
    如果整棵树的异或和为 0,那么我们只需要删除任意一条边,得到两个连通块。因为总异或和为 0,所以这两个连通块的异或和一定相等。题目保证 \(k \ge 2\),所以至少可以删 1 条边。

因此,当 \(S = 0\) 时,直接输出 YES。

  • 情况 2: \(S \ne 0\)
    • 如果 \(k = 2\),我们只能删 1 条边(即分成 2 个连通块)。两个连通块异或和相等的话,它们的异或和必定都是 \(S\),但已知 \(S \ne 0\),矛盾。因此直接输出 NO。
    • 如果 \(k \ge 3\),我们可以删 2 条边,把树分成 3 个连通块。要使 3 个连通块的异或和相等,且总异或和为 \(S\),那么这 3 个连通块的异或和必定都是 \(S\)(因为 \(S \oplus S \oplus S = S\))。
      • 我们在树上进行自底向上的 DFS。对于节点 \(u\),计算它所在子树的异或和 xr[u]。
      • 如果 xr[u] == S,说明我们找到了一个合法的连通块。我们将答案计数 cnt++,并将 xr[u] 置为 0,相当于把它从树中切掉,不再影响父节点。
      • 最后如果 cnt >= 3,说明可以分出来至少 3 个异或和为 \(S\) 的连通块,剩余的碎片异或和一定是 0(可以并入其他块),所以输出 YES。否则输出 NO。

复杂度

  • 时间复杂度:\(O(n)\)。只需要一次 DFS 即可遍历整棵树。
  • 空间复杂度:\(O(n)\)。需要存储树的邻接表以及相关数组。

具体见代码。

\(\boxed{\text{Code}}\)

/*

writer:  Luna_Frome_Starry
Problem: Bakry and Partitioning
Knowledge: DFS
Time: O(n)

*/
/*
Note:

*/
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 200005;
const int M = 100005;
const int INF = 1e9;
const int mod = 998244353;
int head[N], nxt[N * 2], to[N * 2], edge_cnt;
int a[N], xr[N]; //原数组&子树的异或和数组
int n, k, sum_xor, cnt;
void add_edge(int u, int v){ //链式前向星
    to[++edge_cnt] = v;
    nxt[edge_cnt] = head[u];
    head[u] = edge_cnt;
}
void dfs(int u, int f){
    xr[u] = a[u]; //u在自己的子树中
    for(int i = head[u]; i; i = nxt[i]){
        int v = to[i];
        if(v == f){
            continue;
        }
        dfs(v, u);
        xr[u] ^= xr[v]; //计算xr[u]
    }
    if(xr[u] == sum_xor){ //记录cnt
        cnt++;
        xr[u] = 0;
    }
}
void work(){
    cin >> n >> k;
    for(int i = 1; i <= n; i++){
        head[i] = 0;
    }
    edge_cnt = 0;
    sum_xor = 0;
    for(int i = 1; i <= n; i++){
        cin >> a[i];
        sum_xor ^= a[i];
    }
    for(int i = 1; i < n; i++){
        int u, v;
        cin >> u >> v;
        add_edge(u, v);
        add_edge(v, u);
    }
    if(sum_xor == 0){  //判断
        cout << "YES\n";
        return ;
    }
    if(k == 2){
        cout << "NO\n";
        return ;
    }
    cnt = 0;
    dfs(1, 0);
    if(cnt >= 3){
        cout << "YES\n";
    }else{
        cout << "NO\n";
    }
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int oT_To = 1;
    cin >> oT_To; //神秘多组
    while(oT_To--){
        work();
    }
    return 0; //不要忘了
}

AC记录

posted @ 2026-10-21 10:45  Luna_From_Starry  阅读(5)  评论(0)    收藏  举报