CF620 E. New Year Tree(DFS序+状态压缩+线段树)

博客介绍了如何解决Codeforces CF620E问题,涉及树形结构转线性结构、DFS序、状态压缩和线段树的应用。题目要求处理一棵节点染色的树,进行节点颜色更新和查询不同颜色数量的子树。解决方案包括用DFS序排列树,线段树维护区间,并利用位运算统计不同颜色的数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:CF620E
题意: 给你一颗n个节点的树,每个节点被染上了颜色,然后就是m次查询。 查询的方式有两种 1,将以z为根的子树的结点全部更新为颜色X 2,问以z为根的子树的结点的不同颜色数量。默认根为1
Input
第一行输入n,m(4*10^5) 第二行 输入每个节点的颜色(n个) 颜色X<=60 接下来n-1行就是两个点相连 最后m行查询 其中 1 z X 代表操作1, 2 x 代表操作2
Output
输出每次询问的颜色数量

思路:
首先肯定要把树型结构转换为线性结构,所以先用DFS序跑一遍树,将树型结构转换为线性结构,因为树已经变为线性结构所以可以用线段树维护区间,接下来只要解决统计颜色的问题了,这里可以用到二进制的思想,题目说了颜色种数不超过60,所以可以用一个long long的数,每个二进制位代表一个颜色,例如颜色1就是(1<<1)=2,颜色2就是(1<<2)=4,那么如何将两个节点的颜色统计到一个数里面呢,可以知道不同的颜色种数等于两个节点相同的颜色种数+两个节点不同颜色的颜色种数,用到位运算就是(a&b)+(a^b).
代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ULL unsigned long long
#define LL long long
#define Max 1000005
#define mem(a,b) memset(a,b,sizeof(a));
#define pb push_back
#define mp make_pair
#define input ios::sync_with_stdio(false);cin.tie(0);
#define debug printf("debug!!!\n");
const LL mod=1e9+7;
const ULL base=131;
const LL LL_MAX=9223372036854775807;
using namespace std;
int in[4*Max],out[4*Max],num[4*Max],tim;
struct node{
    int to,next;
}edge[4*Max];
int head[4*Max],tot;
int c[Max*4];
LL tree[Max*4],lazy[Max*4];
int n,m;
void init(){
    tot=0;
    mem(head,-1);
}
void addedge(int u,int v){
    edge[tot].to=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}
void dfs(int u,int fa){
    in[u]=++tim;
    num[tim]=u;
    for(int t=head[u];t!=-1;t=edge[t].next){
        int to=edge[t].to;
        if(to!=fa)
            dfs(to,u);
    }
    out[u]=tim;
}
void build(int root,int l,int r){
    if(l==r){
        tree[root]=(LL)1<<c[num[l]];
        return ;
    }
    int mid=(l+r)/2;
    build(2*root,l,mid);
    build(2*root+1,mid+1,r);
    tree[root]=(LL)(tree[2*root]&tree[2*root+1])+(LL)(tree[2*root]^tree[2*root+1]);
}
inline void pushdown(int root){
    if(lazy[root]!=0){
        tree[2*root]=lazy[root];
        tree[2*root+1]=lazy[root];
        lazy[2*root]=lazy[root];
        lazy[2*root+1]=lazy[root];
        lazy[root]=0;
    }
}
void update(int root,int l,int r,int L,int R,int val){
    if(l>=L && r<=R){
        tree[root]=(LL)1<<val;
        lazy[root]=(LL)1<<val;
        return ;
    }
    pushdown(root);
    int mid=(l+r)/2;
    if(L<=mid)
        update(2*root,l,mid,L,R,val);
    if(R>mid)
        update(2*root+1,mid+1,r,L,R,val);
    tree[root]=(LL)(tree[2*root]&tree[2*root+1])+(LL)(tree[2*root]^tree[2*root+1]);
}
LL query(int root,int l,int r,int L,int R){
    if(l>=L && r<=R)
        return tree[root];
    pushdown(root);
    int mid=(l+r)/2;
    LL ans1=0,ans2=0;
    if(L<=mid){
         ans1=query(2*root,l,mid,L,R);
    }
    if(R>mid){
        ans2=query(2*root+1,mid+1,r,L,R);
    }
    return (LL)(ans1&ans2)+(LL)(ans1^ans2);
}
int main()
{
    init();
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&c[i]);
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        addedge(u,v);
        addedge(v,u);
    }
    dfs(1,0);
    build(1,1,n);
    while(m--){
        int opt;
        scanf("%d",&opt);
        if(opt==1){
            int root,c;
            scanf("%d%d",&root,&c);
            update(1,1,n,in[root],out[root],c);
        }else{
            int root;
            scanf("%d",&root);
            LL ans=query(1,1,n,in[root],out[root]);
            int cot=0;
            while(ans){
                    cot++;
                ans&=ans-1;
            }
            printf("%d\n",cot);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值