题目大意:给你一棵 $n$个点 以 $1$为根 的树,每个点有$ 0,1,2 $三种颜色之一,初始时整棵树的颜色均为 $0$。
$m$ 次操作, 每次操作形如:
1 x y c : 将 $x$到$y$的路径上的点全部改为颜色$C$
2 x : 询问 $x$ 所在的同色连通块大小
数据范围:$n,m≤10^5$。
此题一眼动态dp
首先我们先列出正常的dp式子
设$f[u]$表示以$u$为根的子树中,$u$所在的同色联通块大小
显然,$f[u]=1+sum_{v∈son[u],col[u]=col[v]} f[v]$
若需要输出某个点$x$的答案,我们需要输出$f[y]$,其中$y$是满足$x$至$y$颜色相同的最远祖先。
下面考虑动态dp
我们用树链剖分把原树剖成若干条链,每条链用线段树维护,对于线段树每个节点,我们维护七个变量:
设线段树某个节点表示的区间为$[l_k,r_k]$,这个区间中对应原树的节点编号为$rec[l_k],rec[l_k+1]....rec[r_k]$。
$sum[i]$($0≤i≤2$)表示当前的区间中,颜色为$i$的节点的个数。
$cnt[i]$($0≤i≤2$)表示原树中所有与$rec[l_k....r_k]$相连的,且颜色为$i$的轻儿子中,满足以这些儿子为根的子树中,这些点所在的同色联通快大小之和。
$tag$为涂色标记,表示区间$[l_k,r_k]$中的点是否被刷成了同一个颜色。
首先考虑查询$x$所在的联通块大小,令$id[x]$表示节点x在原树中的$dfs$序,$col[x]$表示第$x$个节点当前的颜色,$top[x]$表示$x$所在重链链顶节点编号
我们首先在$x$所在的重链上,查询出$L$和$R$,满足$L≤x≤R$,且节点$rec[L],rec[L+1]...rec[x]...rec[R]$的颜色是一样的,答案显然要累加上这一段节点的贡献。
查询这部分的贡献我们可以在线段树上向x两侧二分查找即可,详见代码。
然后我们需要加上所有与区间$[L,R]$相连的轻链上的贡献。查询这部分信息直接在线段树上将$cnt$的信息累加一下就可以了。
我们目前只统计了x所在重链的情况,上方的重链我们尚未统计
若$rec[L]=top[x]$,说明$x$所在的联通块可能会与$top[x]$上方的节点相连,这个时候需要去统计下上方的贡献。
否则直接退出就可以了。
下面考虑修改操作
我们按照正常树剖的操作来处理,假设我们当前要更改$[x',x]$的颜色
我们显然现在线段树上对这一个区间更新一下颜色。
然后我们发现,这么操作的话,以$top[x]$为根的树种,$top[x]$所在同色联通块大小可能会变,这个时候我们需要重新求一下“以$top[x]$为根,$top[x]$所在同色联通块大小”,并将这个值上传更新上一条链的cnt值。
(详见代码)
这里有一个要注意的地方,更新区间的颜色可以只更新到$lca(x,y)$,但是更新联通块大小必须更新到根(场上错在这里)
然后就没有了
注意细节!
1 #include<bits/stdc++.h> 2 #define M 100005 3 #define mid ((a[x].l+a[x].r)>>1) 4 using namespace std; 5 6 struct seg{int l,r,tag,sum[3],cnt[3];}a[M<<2]={0}; 7 void build(int x,int l,int r){ 8 a[x].l=l; a[x].r=r; a[x].tag=-1; if(l==r) return; 9 build(x<<1,l,mid); build(x<<1|1,mid+1,r); 10 } 11 void upd(int x,int k){ 12 a[x].sum[0]=a[x].sum[1]=a[x].sum[2]=0; 13 a[x].tag=k; a[x].sum[k]=a[x].r-a[x].l+1; 14 } 15 void pushdown(int x){ 16 if(a[x].tag!=-1) upd(x<<1,a[x].tag),upd(x<<1|1,a[x].tag); 17 a[x].tag=-1; 18 } 19 void pushup(int x){ 20 for(int i=0;i<3;i++){ 21 a[x].sum[i]=a[x<<1].sum[i]+a[x<<1|1].sum[i]; 22 a[x].cnt[i]=a[x<<1].cnt[i]+a[x<<1|1].cnt[i]; 23 } 24 } 25 void updatacol(int x,int l,int r,int col){ 26 if(l<=a[x].l&&a[x].r<=r) return upd(x,col); 27 pushdown(x); 28 if(l<=mid) updatacol(x<<1,l,r,col); 29 if(mid<r) updatacol(x<<1|1,l,r,col); 30 pushup(x); 31 } 32 void updatacnt(int x,int id,int col,int val){ 33 if(a[x].l==a[x].r)return void(a[x].cnt[col]+=val); 34 pushdown(x); 35 if(id<=mid) updatacnt(x<<1,id,col,val); 36 else updatacnt(x<<1|1,id,col,val); 37 pushup(x); 38 } 39 int querycnt(int x,int l,int r,int col){ 40 if(l<=a[x].l&&a[x].r<=r) return a[x].cnt[col]; 41 pushdown(x); int res=0; 42 if(l<=mid) res+=querycnt(x<<1,l,r,col); 43 if(mid<r) res+=querycnt(x<<1|1,l,r,col); 44 return res; 45 } 46 int querycol(int x,int id){ 47 if(a[x].l==a[x].r){ 48 if(a[x].sum[0]) return 0; 49 if(a[x].sum[1]) return 1; 50 return 2; 51 } 52 pushdown(x); 53 if(id<=mid) return querycol(x<<1,id); 54 return querycol(x<<1|1,id); 55 } 56 int queryUP(int x,int l,int r,int col){ 57 if(l<=a[x].l&&a[x].r<=r){ 58 if(a[x].sum[col]==a[x].r-a[x].l+1) return a[x].l; 59 } 60 if(a[x].l==a[x].r) return 0; 61 pushdown(x); 62 if(mid<r){ 63 int res=queryUP(x<<1|1,l,r,col); 64 if(res!=mid+1) return res; 65 int res2=0; 66 if(l<=mid) res2=queryUP(x<<1,l,r,col); 67 if(res2) return res2; 68 return res; 69 } 70 return queryUP(x<<1,l,r,col); 71 } 72 int queryDN(int x,int l,int r,int col){ 73 if(l<=a[x].l&&a[x].r<=r){ 74 if(a[x].sum[col]==a[x].r-a[x].l+1) return a[x].r; 75 } 76 if(a[x].l==a[x].r) return 0; 77 pushdown(x); 78 if(l<=mid){ 79 int res=queryDN(x<<1,l,r,col); 80 if(res!=mid) return res; 81 int res2=0; 82 if(mid<r) res2=queryDN(x<<1|1,l,r,col); 83 if(res2) return res2; 84 return res; 85 } 86 return queryDN(x<<1|1,l,r,col); 87 } 88 89 struct edge{int u,next;}e[M]={0}; int head[M]={0},use=0; 90 void add(int x,int y){use++;e[use].u=y;e[use].next=head[x];head[x]=use;} 91 int fa[M]={0},id[M]={0},siz[M]={0},son[M]={0},dep[M]={0},top[M]={0},dn[M]={0},t=0,n,m; 92 int last[M]={0},rec[M]={0}; 93 94 void dfs(int x){ 95 siz[x]=1; 96 for(int i=head[x];i;i=e[i].next){ 97 fa[e[i].u]=x; dep[e[i].u]=dep[x]+1; 98 dfs(e[i].u); 99 siz[x]+=siz[e[i].u]; 100 if(siz[son[x]]<siz[e[i].u]) son[x]=e[i].u; 101 } 102 } 103 void dfs(int x,int Top){ 104 top[x]=Top; id[x]=++t; rec[t]=x; 105 if(son[x]){ 106 dfs(son[x],Top); 107 dn[x]=dn[son[x]]; 108 }else{ 109 updatacol(1,id[Top],id[x],0); 110 dn[x]=x; 111 } 112 for(int i=head[x];i;i=e[i].next) if(e[i].u!=son[x]){ 113 dfs(e[i].u,e[i].u); 114 updatacnt(1,id[x],0,last[e[i].u]=siz[e[i].u]); 115 } 116 } 117 118 int Query(int x,int col){ 119 if(!x) return 0; 120 int l=id[top[x]],r=id[dn[x]]; 121 int L=queryUP(1,l,id[x],col); 122 int R=queryDN(1,id[x],r,col); 123 int res=(R-L+1)+querycnt(1,L,R,col); 124 if(rec[L]==top[x]&&fa[top[x]]&&querycol(1,id[fa[top[x]]])==col) 125 return Query(fa[top[x]],col); 126 return res; 127 } 128 129 void Updata(int x,int y,int col,int chg){ 130 if(!x) return; 131 int Lastcol; 132 if(top[x]==top[y]){ 133 if(dep[x]<dep[y]) swap(x,y); 134 Lastcol=querycol(1,id[top[x]]); 135 if(chg) updatacol(1,id[y],id[x],col); 136 }else{ 137 if(dep[top[x]]<dep[top[y]]) swap(x,y); 138 Lastcol=querycol(1,id[top[x]]); 139 if(chg) updatacol(1,id[top[x]],id[x],col); 140 } 141 int Topcol=querycol(1,id[top[x]]); 142 int L=id[top[x]]; 143 int R=queryDN(1,L,id[dn[x]],Topcol); 144 int val=(R-L+1)+querycnt(1,L,R,Topcol); 145 if(fa[top[x]]){ 146 updatacnt(1,id[fa[top[x]]],Lastcol,-last[top[x]]); 147 updatacnt(1,id[fa[top[x]]],Topcol,val); 148 } 149 last[top[x]]=val; 150 if(top[x]!=top[y]) Updata(fa[top[x]],y,col,1); 151 else Updata(fa[top[x]],fa[top[x]],col,0); 152 } 153 154 int main(){ 155 scanf("%d%d",&n,&m); 156 build(1,1,n); 157 for(int i=2,x;i<=n;i++) scanf("%d",&x),add(x,i); 158 dfs(1); dfs(1,1); 159 while(m--){ 160 int op,x,y,col; scanf("%d%d",&op,&x); 161 if(op==2) printf("%d ",Query(x,querycol(1,id[x]))); 162 else{ 163 scanf("%d%d",&y,&col); 164 Updata(x,y,col,1); 165 } 166 } 167 }