Codeforces 620 E. New Year Tree

16 篇文章 0 订阅

传送门:
http://codeforces.com/problemset/problem/620/E
题意:
一个树,给两种操作,第一种是将一个节点下的所有子树染成一种颜色,第二个操作 是输出一个节点下的子树一共有多少种颜色,多次查询。

这题其实就是裸的线段树区间修改和查询,从颜色只有60种入手,可以用或和的形式存下以每个结点为根的子树种颜色的情况,然后根据时间戳给每个结点标志出区间左右端点,然后套板就ok了,区间修改的时候首先要找到那个区间所对应的结点的编号,然后加标记将其左右两个儿子修改了,可以在查询的过程中继续修改。。。。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX=4e5+9;
ll seg[MAX*4],lazy[MAX*4];
int s[MAX],f[MAX],t,c[MAX],k,v,cc,n,m;
vector<int> g[MAX];
void dfs(int v,int p=-1)
{   
    s[v]=t++;
    for (auto u:g[v]) if (u!=p) dfs(u,v);
    f[v]=t;
}
void shift(int id)
{
    if (lazy[id]) seg[id*2+1]=seg[id*2+2]=lazy[id*2+1]=lazy[id*2+2]=lazy[id];
    lazy[id]=0;
}
void upd(int l,int r,ll val,int s=0,int e=n,int id=0)
{
    if (r<=s || e<=l) return ;
    if (l<=s && e<=r) {seg[id]=lazy[id]=val; return ;}
    int mid=s+e>>1;
    shift(id);
    upd(l,r,val,s,mid,id*2+1),upd(l,r,val,mid,e,id*2+2);
    seg[id]=seg[id*2+1]|seg[id*2+2];
}
ll get(int l,int r,int s=0,int e=n,int id=0)
{
    if (r<=s || e<=l) return 0;
    if (l<=s && e<=r) return seg[id];
    int mid=s+e>>1;
    shift(id);
    return get(l,r,s,mid,id*2+1)|get(l,r,mid,e,id*2+2);
}
int main()
{
    ios_base::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    cin>>n>>m;
    for (int i=0;i<n;i++) cin>>c[i];
    for (int i=0,v,u;i<n-1;i++) cin>>v>>u,v--,u--,g[v].push_back(u),g[u].push_back(v);
    dfs(0);
    for (int i=0;i<n;i++) upd(s[i],s[i]+1,1LL<<c[i]);
    while (m--)
    {
        cin>>k>>v,v--;
        if (k==1) cin>>cc,upd(s[v],f[v],1LL<<cc);
        else cout<<__builtin_popcountll(get(s[v],f[v]))<<'\n';
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值