[JZOJ5512] 送你一棵圣诞树

Description

一棵 n 个点的树, 树根为 1.
一开始每个点上有一个 1…n 的颜色 ci, 不同点颜色可以相同.
现在有 q 次操作, 分为两种类型:
• 1 u l r: 询问子树 u 中有多少种在 l 到 r 之间的颜色至少出现了一次
• 2 u c: 将 u 的颜色修改为 c

要求强制在线
n,q<=100000

Solution

这题先不要转到DFS序列上做

考虑把同一种颜色的点抽出来,在这个点上+1,在DFS序相邻的两个点的LCA上-1,可以开N个set实现

那么就变成了子树求和问题
此时再转到DFS序列上q,区间的区间求和,也就是二维矩形求和,可以树状数组套线段树实现,树状数组每个节点对应一棵线段树,注意动态开点。

修改相当于查询前一个和后一个,set中二分即可

复杂度 O((N+Q)log2N)

Code

#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <set>
#define N 200005
#define M 36000005
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
int dfn[N],dfw[N],sz[N],nt[2*N],dt[2*N],fs[N],n,m,c[N],q,tq,n1,t[M][2],lst[N],nxt[N],rt[N],cl[N],s1[M],c2[N],f[2*N][20],dep[N];
set<int> cr[N];
void dfs(int k,int fa)
{
    sz[k]=1;
    f[k][0]=fa;
    dfn[k]=++dfn[0];
    dep[k]=dep[fa]+1;
    dfw[dfn[k]]=k;
    for(int i=fs[k];i;i=nt[i])
    {
        int p=dt[i];
        if(p!=fa) dfs(p,k),sz[k]+=sz[p];
    }
}
void link(int x,int y)
{
    nt[++m]=fs[x];
    dt[fs[x]=m]=y;
}
void up(int k)
{
    s1[k]=s1[t[k][0]]+s1[t[k][1]];
}
void ins(int k,int l,int r,int w,int v)
{
    if(l==r&&l==w) s1[k]+=v;
    else
    {
        int mid=(l+r)/2;
        if(!t[k][0]) t[k][0]=++n1,t[k][1]=++n1;
        if(w<=mid) ins(t[k][0],l,mid,w,v);
        else ins(t[k][1],mid+1,r,w,v);
        up(k);
    }
}
int sum(int k,int l,int r,int x,int y)
{
    x=max(x,l),y=min(y,r);
    if(x>y||k==0||(s1[k]==0&&!t[k][0])) return 0;
    if(l==x&&r==y) return s1[k];
    else
    {
        int mid=(l+r)/2;
        return sum(t[k][0],l,mid,x,y)+sum(t[k][1],mid+1,r,x,y);
    }
}
int lowbit(int k)
{
    return k&(-k);
}
void put(int k,int c,int v)
{
    if(k)
    while(k<=n)
    {
        if(!rt[k]) rt[k]=++n1;
        ins(rt[k],1,n,c,v);
        k+=lowbit(k);
    }
}
int get(int k,int x,int y)
{
    int s=0;
    while(k)
    {
        if(!rt[k]) rt[k]=++n1;
        s+=sum(rt[k],1,n,x,y);
        k-=lowbit(k);
    }
    return s;
}
int lca(int x,int y)
{
    if(dep[x]>dep[y]) swap(x,y);
    int j=19;
    while(dep[y]>dep[x])
    {
        while(j&&dep[f[y][j]]<dep[x]) j--;
        y=f[y][j];
    }
    j=19;
    while(x!=y)
    {
        while(j&&f[x][j]==f[y][j]) j--;
        x=f[x][j],y=f[y][j];
    }
    return x;
}
int main()
{
    freopen("xmastree1.in","r",stdin);
    freopen("xmastree1.out","w",stdout);
    cin>>n>>q>>tq;
    fo(i,1,n) scanf("%d",&c[i]);
    fo(i,1,n-1)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        link(x,y),link(y,x);
    }
    dfs(1,0);
    fo(j,1,19)
        fo(i,1,n) f[i][j]=f[f[i][j-1]][j-1];
    fo(i,1,n) cr[i].insert(n+1),cr[i].insert(0);
    fo(i,1,n) 
    {
        cr[c[i]].insert(dfn[i]);
        lst[i]=cl[c[dfw[i]]];
        nxt[lst[i]]=i;
        cl[c[dfw[i]]]=i;
        if(lst[i]) put(dfn[lca(dfw[lst[i]],dfw[i])],c[dfw[i]],-1);
        put(i,c[dfw[i]],1);
    }
    int last=0;
    fo(i,1,q)
    {
        int p,x,y,t;
        scanf("%d%d",&p,&t);
        if(tq) t^=last;
        int t1=t;
        if(p==1) 
        {
            scanf("%d%d",&x,&y);
            if(tq) x^=last,y^=last;
            last=get(dfn[t]+sz[t]-1,x,y)-get(dfn[t]-1,x,y);
            printf("%d\n",last);
        } 
        else
        {
            scanf("%d",&x); 
            if(tq) x^=last;
            if(x==c[t]) continue;
            cr[c[t]].erase(dfn[t]);
            if(lst[dfn[t]]) put(dfn[lca(dfw[lst[dfn[t]]],t)],c[t],1);
            put(dfn[t],c[t],-1);
            lst[nxt[dfn[t]]]=lst[dfn[t]];
            nxt[lst[dfn[t]]]=nxt[dfn[t]];
            nxt[0]=lst[0]=0;
            if(nxt[dfn[t]]) 
            {
                put(dfn[lca(t,dfw[nxt[dfn[t]]])],c[t],1);
                if(lst[dfn[t]]) put(dfn[lca(dfw[nxt[dfn[t]]],dfw[lst[dfn[t]]])],c[t],-1);
            }
            c[t]=x;
            cr[x].insert(dfn[t]);
            set<int>::iterator it=cr[x].lower_bound(dfn[t]);            
            --it;
            if((*it)==0) lst[dfn[t]]=0;
            else
            {
                lst[dfn[t]]=*it;
                put(dfn[lca(dfw[lst[dfn[t]]],t)],x,-1);
                nxt[lst[dfn[t]]]=dfn[t];
            }
            put(dfn[t],x,1);
            it=cr[x].upper_bound(dfn[t]);
            if((*it)==n+1) nxt[dfn[t]]=0;
            else 
            {
                nxt[dfn[t]]=*it;
                put(dfn[lca(t,dfw[nxt[dfn[t]]])],x,-1);
                if(lst[dfn[t]]) put(dfn[lca(dfw[lst[dfn[t]]],dfw[nxt[dfn[t]]])],x,1);
                lst[nxt[dfn[t]]]=dfn[t];
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值