Codeforces620 E. New Year Tree(dfs序建线段树+状压)

题意:

给定n个顶点的树,每个点上有一个颜色c(i),
m次操作,操作有两种:
(1,x,y):将x为根的子树所有点颜色置为y
(2,x):查询x为根的子树有多少种不同的颜色

数据范围:n<=4e5,1<=c(i)<=60

解法:

子树问题用dfs序转化为序列问题,
观察到颜色只有60种,显然状压维护颜色数,
这题只有区间覆盖,区间颜色合并,用线段树维护即可。

code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define PI pair<int,int>
const int maxm=2e6+5;
vector<int>g[maxm];
int L[maxm],R[maxm],rk[maxm],idx;
int col[maxm];
int n,m;
struct Tree{
    int a[maxm<<2];
    int laz[maxm<<2];
    void pp(int node){
        a[node]=a[node*2]|a[node*2+1];
    }
    void pd(int node){
        if(laz[node]){
            a[node*2]=(1LL<<laz[node]);
            a[node*2+1]=(1LL<<laz[node]);
            laz[node*2]=laz[node];
            laz[node*2+1]=laz[node];
            laz[node]=0;
        }
    }
    void build(int l,int r,int node){
        if(l==r){
            a[node]=(1LL<<col[rk[l]]);
            return ;
        }
        int mid=(l+r)/2;
        build(l,mid,node*2);
        build(mid+1,r,node*2+1);
        pp(node);
    }
    void update(int st,int ed,int val,int l,int r,int node){
        if(st<=l&&ed>=r){
            a[node]=(1LL<<val);
            laz[node]=val;
            return ;
        }
        pd(node);
        int mid=(l+r)/2;
        if(st<=mid)update(st,ed,val,l,mid,node*2);
        if(ed>mid)update(st,ed,val,mid+1,r,node*2+1);
        pp(node);
    }
    int ask(int st,int ed,int l,int r,int node){
        if(st<=l&&ed>=r)return a[node];
        pd(node);
        int mid=(l+r)/2;
        int ans=0;
        if(st<=mid)ans|=ask(st,ed,l,mid,node*2);
        if(ed>mid)ans|=ask(st,ed,mid+1,r,node*2+1);
        return ans;
    }
}T;
int cal(int x){
    int ans=0;
    while(x){
        ans++;
        x&=(x-1);
    }
    return ans;
}
void dfs(int x,int fa){
    L[x]=++idx;
    rk[idx]=x;
    for(int v:g[x]){
        if(v==fa)continue;
        dfs(v,x);
    }
    R[x]=idx;
}
signed main(){
    ios::sync_with_stdio(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>col[i];
    for(int i=1;i<n;i++){
        int a,b;cin>>a>>b;
        g[a].push_back(b);
        g[b].push_back(a);
    }
    dfs(1,1);
    T.build(1,n,1);
    while(m--){
        int op;cin>>op;
        if(op==1){
            int x,c;cin>>x>>c;
            T.update(L[x],R[x],c,1,n,1);
        }else{
            int x;cin>>x;
            int ans=T.ask(L[x],R[x],1,n,1);
            ans=cal(ans);
            cout<<ans<<endl;
        }
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值