jzoj5512 送你一棵圣诞树 树状数组套线段树+set

25 篇文章 0 订阅
17 篇文章 0 订阅

Description


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

对于前 20% 的数据, n, q ≤ 5000;
对于前 40% 的数据, n, q ≤ 50000;
对于另 20% 的数据, 没有修改操作;
对于另 20% 的数据, t = 0;
对于 100% 的数据, 1 ≤ n, q ≤ 10000

Solution


大数据结构题,有点恶心
首先单独考虑1种颜色,即x点涂上颜色后会对x至根的路径上所有点查询造成1的贡献,
而颜色相同dfs序相邻的两个点的lca至根的路径则被多算了一次因此要减去1。注意这里一定是dfs序相邻的才能满足,具体可以画一下图
多种颜色可以每种颜色开一个set维护他们的dfs序相邻,每次增加就修改前驱后继,删除同理,颜色区间就树状数组套一个线段树即可
实际操作中可以用单点修改+区间求和算一段子树的答案,比第一时间想到的树剖再单点查询好写
set确实好用,mark一下iterator和set重载运算符的技巧以后说不定经常用(flag)

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <set>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define drp(i,st,ed) for (int i=st;i>=ed;--i)
#define lowbit(x) ((x)&(-(x)))
using std:: swap;
const int N=200005;
const int E=400005;
struct edge{int x,y,next;}e[E];
struct treeNode{int l,r,sum;}tree[N*81];
struct data{
    int x,y;
    friend bool operator <(data a,data b) {return a.y<b.y;}
} ymw;
std:: set<data> s[N];
std:: set<data>::iterator it;
int size[N],dep[N],pos[N],bl[N],fa[N][21],c[N],tmp[N],wer[N];
int root[N],cnt,n;
int ls[N],edCnt=0;
int read() {
    int x=0,v=1; char ch=getchar();
    for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
    for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
    return x*v;
}
void addEdge(int x,int y) {
    e[++edCnt]=(edge){x,y,ls[x]}; ls[x]=edCnt;
    e[++edCnt]=(edge){y,x,ls[y]}; ls[y]=edCnt;
}
void dfs(int now) {
    pos[now]=++pos[0];
    wer[pos[0]]=now;
    rep(i,1,20) fa[now][i]=fa[fa[now][i-1]][i-1];
    size[now]=1;
    for (int i=ls[now];i;i=e[i].next) {
        if (fa[now][0]==e[i].y) continue;
        fa[e[i].y][0]=now;
        dep[e[i].y]=dep[now]+1;
        dfs(e[i].y);
        size[now]+=size[e[i].y];
    }
}
int get_lca(int x,int y) {
    if (dep[x]<dep[y]) swap(x,y);
    drp(i,20,0) if (dep[fa[x][i]]>=dep[y]) x=fa[x][i];
    drp(i,20,0) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    if (x!=y) x=fa[x][0];
    return x;
}
int query(int now,int tl,int tr,int l,int r) {
    if (!now) return 0;
    if (tl==l&&tr==r) return tree[now].sum;
    int mid=(tl+tr)>>1;
    if (r<=mid) return query(tree[now].l,tl,mid,l,r);
    else if (l>mid) return query(tree[now].r,mid+1,tr,l,r);
    else return query(tree[now].l,tl,mid,l,mid)+query(tree[now].r,mid+1,tr,mid+1,r);
}
void modify(int &now,int tl,int tr,int x,int v) {
    if (!now) tree[now=++cnt]=(treeNode){0,0,0};
    tree[now].sum+=v;
    if (tl==tr) return ;
    int mid=(tl+tr)>>1;
    if (x<=mid) modify(tree[now].l,tl,mid,x,v);
    else if (x>mid) modify(tree[now].r,mid+1,tr,x,v);
}
int get(int x,int l,int r) {
    int ret=0;
    while (x) {
        ret+=query(root[x],1,n,l,r);
        x-=lowbit(x);
    }
    return ret;
}
void add(int x,int y,int v) {
    while (x<=n) {
        modify(root[x],1,n,y,v);
        x+=lowbit(x);
    }
}
void ins(int x) {
    int lower=0,upper=0;
    ymw=(data){x,pos[x]};
    s[c[x]].insert(ymw);
    it=s[c[x]].find(ymw);
    if (it!=s[c[x]].begin()) {it--; lower=(*it).x; it++;}
    it++; if (it!=s[c[x]].end()) upper=(*it).x;
    if (lower&&upper) add(c[x],pos[get_lca(lower,upper)],1);
    if (lower) add(c[x],pos[get_lca(lower,x)],-1);
    if (upper) add(c[x],pos[get_lca(upper,x)],-1);
    add(c[x],pos[x],1);
}
void del(int x) {
    int lower=0,upper=0;
    ymw=(data){x,pos[x]};
    it=s[c[x]].find(ymw);
    if (it!=s[c[x]].begin()) {it--; lower=(*it).x; it++;}
    it++; if (it!=s[c[x]].end()) upper=(*it).x;
    if (lower&&upper) add(c[x],pos[get_lca(lower,upper)],-1);
    if (lower) add(c[x],pos[get_lca(lower,x)],1);
    if (upper) add(c[x],pos[get_lca(upper,x)],1);
    add(c[x],pos[x],-1);
    s[c[x]].erase(s[c[x]].find(ymw));
}
bool cmp(int x,int y) {return pos[x]<pos[y];}
int main(void) {
    n=read(); int q=read(),wjp=read();
    rep(i,1,n) c[i]=read(),tmp[i]=i,root[i]=++cnt;
    rep(i,2,n) {
        int x=read(),y=read();
        addEdge(x,y);
    }
    dep[1]=1; dfs(1);
    std:: sort(tmp+1,tmp+n+1,cmp);
    rep(i,1,n) {
        ins(tmp[i]);
    }
    int lastans=0;
    while (q--) {
        int opt=read(),x=read(),l=read();
        if (opt==1) {
            int r=read();
            if (wjp) x^=lastans,l^=lastans,r^=lastans;
            int getr=get(r,pos[x],pos[x]+size[x]-1);
            int getl=get(l-1,pos[x],pos[x]+size[x]-1);
            lastans=getr-getl;
            printf("%d\n", getr-getl);
        } else if (opt==2) {
            if (wjp) x^=lastans,l^=lastans;
            del(x);
            c[x]=l;
            ins(x);
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值