• 【CF1017G】The Tree(线段树)


    题目链接

    • 给定一棵 \(n\) 个点的以 \(1\) 为根的树,初始所有点为白色。
    • \(q\) 次操作,分为三种:若 \(x\) 为白色则将其染黑,否则递归对 \(x\) 的所有子节点进行该操作;将 \(x\) 的子树所有点染白;询问 \(x\) 的颜色。
    • \(2\le n\le10^5\)\(1\le q\le10^5\)

    线段树

    挺久以前看到过这道题的,当时胡了一个树链剖分的做法,在每个点上考虑它的祖先们的修改对它的影响,好像要维护一个后缀最小值什么的,不知道出于什么原因当时没有写。

    今天看到 \(\color{black}{2}\color{red}{75307894a}\) 在嘲讽怎么没人写一个 \(\log\) 的做法,才发现这题原来还可以直接用线段树做。


    核心思想在于考虑维护 \(F(x)\) 表示 \(x\) 到根上的白点个数。

    第一个操作就相当于将 \(x\) 子树内到 \(x\) 路径上存在白点的节点的 \(F\) 值都减 \(1\),等价于将 \(x\) 子树内全部 \(F\) 值减 \(1\) 然后向 \(F(fa_x)\)\(\max\)

    第二个操作就相当于将 \(x\) 子树内每个点 \(i\)\(F\) 值修改为 \(F(fa_x)+Dep_i-Dep_{fa_x}\),也就是 \((F(fa_x)-Dep_{fa_x})+Dep_i\),只需打上一个标记 \(F(fa_x)-Dep_{fa_x}\) 即可。

    第三个操作只要询问出 \(F(fa_x)\)\(F(x)\),若 \(F(fa_x)\not=F(x)\) 说明 \(x\) 为白点,否则 \(x\) 为黑点。

    整理一下,我们需要实现区间加、区间取 \(\max\)、区间打标记赋值、单点询问,可以按照赋值、加法、取 \(\max\) 的顺序维护标记。

    代码:\(O(n\log n)\)

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Rg register
    #define RI Rg int
    #define Cn const
    #define CI Cn int&
    #define I inline
    #define W while
    #define N 100000
    #define INF (int)1e9
    #define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
    using namespace std;
    namespace FastIO
    {
    	#define FS 100000
    	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
    	char oc,FI[FS],*FA=FI,*FB=FI;
    	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
    	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    }using namespace FastIO;
    int n,d,fc[N+5],dI[N+5],dO[N+5],D[N+5],f[N+5],ee,lnk[N+5];struct edge {int to,nxt;}e[N+5];
    void dfs(CI x) {fc[dI[x]=++d]=x;for(RI i=lnk[x],y;i;i=e[i].nxt) (y=e[i].to)^f[x]&&(D[y]=D[x]+1,dfs(y),0);dO[x]=d;}//dfs预处理
    class SegmentTree//线段树
    {
    	private:
    		#define PT RI l=1,RI r=n,RI o=1
    		#define LT l,u,o<<1
    		#define RT u+1,r,o<<1|1
    		#define TF(o,v) (F[o]+=v,G[o]^INF&&(G[o]+=v))//加法,并修改取max的标记
    		#define TG(o,v) (G[o]=G[o]^INF?max(G[o],v):v)//取max
    		#define TP(o,v) (F[o]=0,G[o]=INF,P[o]=v)//赋值,清空其他标记
    		#define PD(o) (P[o]^INF&&(TP(o<<1,P[o]),TP(o<<1|1,P[o]),P[o]=INF),\
    			F[o]&&(TF(o<<1,F[o]),TF(o<<1|1,F[o]),F[o]=0),G[o]^INF&&(TG(o<<1,G[o]),TG(o<<1|1,G[o]),G[o]=INF))//按照赋值、加法、取max的顺序下传
    		int F[N<<2],G[N<<2],P[N<<2];
    	public:
    		void UF(CI L,CI R,PT) {if(L<=l&&r<=R) return (void)TF(o,-1);RI u=l+r>>1;PD(o),L<=u&&(UF(L,R,LT),0),R>u&&(UF(L,R,RT),0);}//区间减1
    		void UG(CI L,CI R,CI v,PT) {if(L<=l&&r<=R) return (void)TG(o,v);RI u=l+r>>1;PD(o),L<=u&&(UG(L,R,v,LT),0),R>u&&(UG(L,R,v,RT),0);}//区间取max
    		void UP(CI L,CI R,CI v,PT) {if(L<=l&&r<=R) return (void)TP(o,v);RI u=l+r>>1;PD(o),L<=u&&(UP(L,R,v,LT),0),R>u&&(UP(L,R,v,RT),0);}//区间赋值
    		int Q(CI x,PT) {if(P[o]^INF) return max(D[fc[x]]+P[o]+F[o],G[o]^INF?G[o]:0);RI u=l+r>>1;PD(o);return x<=u?Q(x,LT):Q(x,RT);}//单点询问
    }S;
    int main()
    {
    	RI Qt,i,x,y;for(read(n,Qt),i=2;i<=n;++i) read(f[i]),add(f[i],i);D[1]=1,dfs(1);
    	RI g;W(Qt--) switch(read(y,x),g=x^1?S.Q(dI[f[x]]):0,y)//g存下f[x]的F值,无论哪种操作都会用到
    	{
    		case 1:S.UF(dI[x],dO[x]),S.UG(dI[x],dO[x],g);break;//区间减1,然后向F(f[x])取max
    		case 2:S.UP(dI[x],dO[x],g-D[f[x]]);break;//区间打标记赋值为F(f[x])-D[f[x]]+D[i]
    		case 3:puts(S.Q(dI[x])^g?"white":"black");break;//将F(x)与F(f[x])比较
    	}return 0;
    }
    
  • 相关阅读:
    C# 在代码中创建 DataTable 和从数据库取出的数据 DataTable
    C#编程数据库操作之DataTable
    测试代码的运行时间(C#)
    时间天数 的使用
    遍历panel 上的控件,然后操作
    break 和 continue区别
    DataTable排序的一般方法
    MG758 GIS数据采集终端
    C#中DataTable
    android InputStream相关类
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF1017G.html
Copyright © 2020-2023  润新知