CF1592C Bakry and Partitioning | 异或

传送门

题意

给定一颗大小为\(n\)带点权的树, 和一个整数\(k\)
询问是否可以删除至多\(k-1\)条边(至少1条), 将树分成至多\(k\)个连通块,使得每个连通块中的点权异或和相同

题解

这种题的思路大概就是考虑分成很多连通块时, 是否能用较少的联通块等效代替

容易想到, 如果划分成\(m\)个异或和相同的联通块,当\(m\)为偶数,肯定也能分成两个异或和为0的连通块,
同理, \(m\)为奇数, 也能分成3个相同的连通块

也就是,要么两个, 要么三个, 分类讨论嘛:


  1. 两个的话简单, 肯定将原树和他的一颗子树分开
    枚举子树即可

  2. 三个的话, 也就是说, 我们要将原树的两颗子树分离开
    那么继续讨论, 两颗子树是否相交:
    CF1592C Bakry and Partitioning | 异或

如果不相交的话, 我们假设三颗子树的异或和分别为: \(a, b, c\)
显然:要满足 \(a \oplus (b \oplus c) = b = c\)
因为\(b = c\)得到 \(b \oplus c = 0\)
所以\(a = b = c\)
统计是否存在即可

如果相交的话, 显然: \(a \oplus b = b \oplus c = c\)
得到\(a = c , b = 0\)
依然统计即可

因为a一定为根节点, 上面两个不难统计

#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;

int read(){
    int num=0, flag=1; char c=getchar();
    while(!isdigit(c) && c!='-') c=getchar();
    if(c == '-') c=getchar(), flag=-1;
    while(isdigit(c)) num=num*10+c-'0', c=getchar();
    return num*flag;
}

const int N = 290005;
int T, n;
int A[N], sum[N], a[N], cnt[N], fa[N];
vector<int> p[N];

int flag;
int rflag = 0;

void dp(int x){
    sum[x] = a[x];
    for(auto nex : p[x]) {
        if(nex == fa[x]) continue;
        fa[nex] = x;
        dp(nex);
        sum[x] ^= sum[nex];
    }
}

void dfs(int x){
    int res = 0;
    for(auto nex : p[x]){
        if(nex == fa[x]) continue;
        dfs(nex);
        if(cnt[nex]) res++;
        if(cnt[nex] && sum[x]==0) rflag=1; 
    }
    if(res >= 2) rflag = 1;
    if(sum[x] == sum[1] || res) cnt[x]=1; 
}

int main(){
    T = read();
    while(T--){
        n = read(); int k=read();
        for(int i=1; i<=n; i++) {
            sum[i] = 0;
            cnt[i] = 0;
            p[i].clear();
            a[i] = read();
        }

        for(int i=1; i<n; i++){
            int u=read(), v=read();
            p[u].push_back(v);
            p[v].push_back(u);
        }

        flag = 0; rflag=0;
        dp(1);	
        for(int i=2; i<=n; i++){
            if((sum[1]^sum[i]) == sum[i]) flag=1;
        }

        if(!flag && k>=3){
            dfs(1);
            flag = rflag;
        }

        printf(flag?"YES\n":"NO\n");
    }
    return 0;
}

上一篇:位运算常见套路 总结


下一篇:【题解】CF1408I Bitwise Magic