这里会显示出您选择的修订版和当前版本之间的差别。
| 两侧同时换到之前的修订记录 前一修订版 | |||
|
2020-2021:teams:legal_string:jxm2001:虚树 [2020/10/08 17:04] jxm2001 [题解] |
2020-2021:teams:legal_string:jxm2001:虚树 [2020/10/08 22:49] (当前版本) jxm2001 |
||
|---|---|---|---|
| 行 610: | 行 610: | ||
| </hidden> | </hidden> | ||
| + | ==== 习题四 ==== | ||
| + | |||
| + | [[https://www.luogu.com.cn/problem/P4242|洛谷p4242]] | ||
| + | |||
| + | === 题意 === | ||
| + | |||
| + | 给定一棵树,每个点有一种颜色。定义 $T(u,v)$ 表示点 $u$ 到点 $v$ 路径的颜色段数。接下来 $q$ 个操作。 | ||
| + | |||
| + | - 将路径 $u\to v$ 的所有结点颜色修改为 $c$ | ||
| + | - 给定 $m$ 个点 $a_1,a_2\cdots a_m$。对每个 $i$,询问 $\sum_{j=1}^m T(a_i,a_j)$ | ||
| + | |||
| + | === 题解 === | ||
| + | |||
| + | 建立虚树,同时定义 $u\to v$ 的权值为 $u\to v$ 的颜色段数减去最开始 $u$ 颜色的一端。 | ||
| + | |||
| + | 树剖维护边修改和边询问,注意合并时需要分别维护左路径和右路径。最后点分治统计答案。时间复杂度 $O((\sum m)\log^2 n)$。 | ||
| + | |||
| + | <hidden 查看代码> | ||
| + | <code cpp> | ||
| + | const int MAXN=1e5+5; | ||
| + | namespace T{ | ||
| + | struct Edge{ | ||
| + | int to,next; | ||
| + | }edge[MAXN<<1]; | ||
| + | int head[MAXN],edge_cnt; | ||
| + | void Insert(int u,int v){ | ||
| + | edge[++edge_cnt]=Edge{v,head[u]}; | ||
| + | head[u]=edge_cnt; | ||
| + | edge[++edge_cnt]=Edge{u,head[v]}; | ||
| + | head[v]=edge_cnt; | ||
| + | } | ||
| + | struct Node{ | ||
| + | int lc,rc,sum; | ||
| + | Node(int lc=0,int rc=0,int sum=0):lc(lc),rc(rc),sum(sum){} | ||
| + | Node operator + (const Node &b){ | ||
| + | if(!sum)return b; | ||
| + | if(!b.sum)return *this; | ||
| + | return Node(lc,b.rc,sum+b.sum-(rc==b.lc)); | ||
| + | } | ||
| + | }s[MAXN<<2]; | ||
| + | int a[MAXN],lef[MAXN<<2],rig[MAXN<<2],lazy[MAXN<<2]; | ||
| + | void push_up(int k){s[k]=s[k<<1]+s[k<<1|1];} | ||
| + | void build(int k,int L,int R){ | ||
| + | lef[k]=L,rig[k]=R; | ||
| + | int M=L+R>>1; | ||
| + | if(L==R){ | ||
| + | s[k]=Node(a[M],a[M],1); | ||
| + | return; | ||
| + | } | ||
| + | build(k<<1,L,M); | ||
| + | build(k<<1|1,M+1,R); | ||
| + | push_up(k); | ||
| + | } | ||
| + | void push_tag(int k,int c){ | ||
| + | s[k].lc=s[k].rc=c,s[k].sum=1; | ||
| + | lazy[k]=c; | ||
| + | } | ||
| + | void push_down(int k){ | ||
| + | if(lazy[k]){ | ||
| + | push_tag(k<<1,lazy[k]); | ||
| + | push_tag(k<<1|1,lazy[k]); | ||
| + | lazy[k]=0; | ||
| + | } | ||
| + | } | ||
| + | void update(int k,int L,int R,int v){ | ||
| + | if(L<=lef[k]&&rig[k]<=R){ | ||
| + | push_tag(k,v); | ||
| + | return; | ||
| + | } | ||
| + | push_down(k); | ||
| + | int mid=lef[k]+rig[k]>>1; | ||
| + | if(mid>=L) | ||
| + | update(k<<1,L,R,v); | ||
| + | if(mid<R) | ||
| + | update(k<<1|1,L,R,v); | ||
| + | push_up(k); | ||
| + | } | ||
| + | Node query(int k,int L,int R){ | ||
| + | if(L<=lef[k]&&rig[k]<=R) | ||
| + | return s[k]; | ||
| + | push_down(k); | ||
| + | int mid=lef[k]+rig[k]>>1; | ||
| + | if(mid<L)return query(k<<1|1,L,R); | ||
| + | else if(mid>=R)return query(k<<1,L,R); | ||
| + | else | ||
| + | return query(k<<1,L,R)+query(k<<1|1,L,R); | ||
| + | } | ||
| + | int c[MAXN],d[MAXN],sz[MAXN],f[MAXN],dfn[MAXN],dfs_t; | ||
| + | int h_son[MAXN],mson[MAXN],p[MAXN]; | ||
| + | void dfs_1(int u,int fa,int depth){ | ||
| + | sz[u]=1;f[u]=fa;d[u]=depth;mson[u]=0; | ||
| + | for(int i=head[u];i;i=edge[i].next){ | ||
| + | int v=edge[i].to; | ||
| + | if(v==fa)continue; | ||
| + | dfs_1(v,u,depth+1); | ||
| + | sz[u]+=sz[v]; | ||
| + | if(sz[v]>mson[u]){ | ||
| + | h_son[u]=v; | ||
| + | mson[u]=sz[v]; | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | void dfs_2(int u,int top){ | ||
| + | dfn[u]=++dfs_t;p[u]=top;a[dfs_t]=c[u]; | ||
| + | if(mson[u]) | ||
| + | dfs_2(h_son[u],top); | ||
| + | for(int i=head[u];i;i=edge[i].next){ | ||
| + | int v=edge[i].to; | ||
| + | if(v==f[u]||v==h_son[u])continue; | ||
| + | dfs_2(v,v); | ||
| + | } | ||
| + | } | ||
| + | int query_path(int u,int v){ | ||
| + | Node ansu,ansv; | ||
| + | while(p[u]!=p[v]){ | ||
| + | if(d[p[u]]<d[p[v]]) | ||
| + | swap(u,v),swap(ansu,ansv); | ||
| + | ansu=query(1,dfn[p[u]],dfn[u])+ansu; | ||
| + | u=f[p[u]]; | ||
| + | } | ||
| + | if(d[u]>d[v])swap(u,v),swap(ansu,ansv); | ||
| + | swap(ansu.lc,ansu.rc); | ||
| + | return (ansu+query(1,dfn[u],dfn[v])+ansv).sum-1; | ||
| + | } | ||
| + | void update_path(int u,int v,int w){ | ||
| + | while(p[u]!=p[v]){ | ||
| + | if(d[p[u]]<d[p[v]]) | ||
| + | swap(u,v); | ||
| + | update(1,dfn[p[u]],dfn[u],w); | ||
| + | u=f[p[u]]; | ||
| + | } | ||
| + | if(d[u]>d[v]) | ||
| + | swap(u,v); | ||
| + | update(1,dfn[u],dfn[v],w); | ||
| + | } | ||
| + | int LCA(int u,int v){ | ||
| + | while(p[u]!=p[v]){ | ||
| + | if(d[p[u]]<d[p[v]]) | ||
| + | swap(u,v); | ||
| + | u=f[p[u]]; | ||
| + | } | ||
| + | if(d[u]>d[v]) | ||
| + | swap(u,v); | ||
| + | return u; | ||
| + | } | ||
| + | } | ||
| + | struct Edge{ | ||
| + | int to,next,w; | ||
| + | }edge[MAXN<<1]; | ||
| + | int head[MAXN],edge_cnt; | ||
| + | void Insert(int u,int v,int w){ | ||
| + | edge[++edge_cnt]=Edge{v,head[u],w}; | ||
| + | head[u]=edge_cnt; | ||
| + | edge[++edge_cnt]=Edge{u,head[v],w}; | ||
| + | head[v]=edge_cnt; | ||
| + | } | ||
| + | struct cmp{ | ||
| + | bool operator () (const int a,const int b)const{ | ||
| + | return T::dfn[a]<T::dfn[b]; | ||
| + | } | ||
| + | }; | ||
| + | int Stack[MAXN],top,temp[MAXN],node[MAXN]; | ||
| + | void build(int k){ | ||
| + | edge_cnt=0; | ||
| + | sort(node,node+k,cmp()); | ||
| + | Stack[top=1]=1;head[1]=0; | ||
| + | _for(i,0,k){ | ||
| + | if(node[i]==1)continue; | ||
| + | int p=T::LCA(Stack[top],node[i]); | ||
| + | if(p!=Stack[top]){ | ||
| + | while(T::dfn[p]<T::dfn[Stack[top-1]]) | ||
| + | Insert(Stack[top-1],Stack[top],T::query_path(Stack[top],Stack[top-1])),top--; | ||
| + | if(p!=Stack[top-1]) | ||
| + | head[p]=0,Insert(p,Stack[top],T::query_path(Stack[top],p)),Stack[top]=p; | ||
| + | else | ||
| + | Insert(Stack[top-1],Stack[top],T::query_path(Stack[top],Stack[top-1])),top--; | ||
| + | } | ||
| + | head[node[i]]=0,Stack[++top]=node[i]; | ||
| + | } | ||
| + | while(top>1)Insert(Stack[top-1],Stack[top],T::query_path(Stack[top],Stack[top-1])),top--; | ||
| + | } | ||
| + | bool is_key[MAXN],vis[MAXN]; | ||
| + | LL ans[MAXN],slen[MAXN],s1; | ||
| + | int sz[MAXN],mson[MAXN],tot_sz,root,root_sz,s2; | ||
| + | void find_root(int u,int fa){ | ||
| + | sz[u]=1;mson[u]=0; | ||
| + | for(int i=head[u];i;i=edge[i].next){ | ||
| + | int v=edge[i].to; | ||
| + | if(vis[v]||v==fa) | ||
| + | continue; | ||
| + | find_root(v,u); | ||
| + | sz[u]+=sz[v]; | ||
| + | mson[u]=max(mson[u],sz[v]); | ||
| + | } | ||
| + | mson[u]=max(mson[u],tot_sz-sz[u]); | ||
| + | if(mson[u]<root_sz){ | ||
| + | root=u; | ||
| + | root_sz=mson[u]; | ||
| + | } | ||
| + | } | ||
| + | void dfs_1(int u,int fa,int len){ | ||
| + | if(is_key[u]) | ||
| + | sz[u]=1,slen[u]=len; | ||
| + | else | ||
| + | sz[u]=slen[u]=0; | ||
| + | for(int i=head[u];i;i=edge[i].next){ | ||
| + | int v=edge[i].to; | ||
| + | if(vis[v]||v==fa) | ||
| + | continue; | ||
| + | dfs_1(v,u,len+edge[i].w); | ||
| + | sz[u]+=sz[v]; | ||
| + | slen[u]+=slen[v]; | ||
| + | } | ||
| + | } | ||
| + | void dfs_2(int u,int fa,int len){ | ||
| + | if(is_key[u])ans[u]+=s1+1LL*len*s2; | ||
| + | for(int i=head[u];i;i=edge[i].next){ | ||
| + | int v=edge[i].to; | ||
| + | if(vis[v]||v==fa) | ||
| + | continue; | ||
| + | dfs_2(v,u,len+edge[i].w); | ||
| + | } | ||
| + | } | ||
| + | void query(int u){ | ||
| + | dfs_1(u,0,1); | ||
| + | ans[u]+=slen[u]; | ||
| + | for(int i=head[u];i;i=edge[i].next){ | ||
| + | int v=edge[i].to; | ||
| + | if(vis[v]) | ||
| + | continue; | ||
| + | s1=slen[u]-slen[v]; | ||
| + | s2=sz[u]-sz[v]; | ||
| + | dfs_2(v,u,edge[i].w); | ||
| + | } | ||
| + | } | ||
| + | void solve(int u){ | ||
| + | int cur_sz=tot_sz; | ||
| + | vis[u]=true;query(u); | ||
| + | for(int i=head[u];i;i=edge[i].next){ | ||
| + | int v=edge[i].to; | ||
| + | if(vis[v]) | ||
| + | continue; | ||
| + | tot_sz=sz[v]>sz[u]?cur_sz-sz[u]:sz[v];root_sz=MAXN; | ||
| + | find_root(v,u); | ||
| + | solve(root); | ||
| + | } | ||
| + | } | ||
| + | void dfs_3(int u,int fa){ | ||
| + | tot_sz++; | ||
| + | for(int i=head[u];i;i=edge[i].next){ | ||
| + | int v=edge[i].to; | ||
| + | if(v==fa)continue; | ||
| + | dfs_3(v,u); | ||
| + | } | ||
| + | } | ||
| + | void dfs_4(int u,int fa){ | ||
| + | ans[u]=0,vis[u]=is_key[u]=false; | ||
| + | for(int i=head[u];i;i=edge[i].next){ | ||
| + | int v=edge[i].to; | ||
| + | if(v==fa)continue; | ||
| + | dfs_4(v,u); | ||
| + | } | ||
| + | } | ||
| + | int main() | ||
| + | { | ||
| + | int n=read_int(),q=read_int(),rt=1; | ||
| + | _rep(i,1,n)T::c[i]=read_int(); | ||
| + | _for(i,1,n){ | ||
| + | int u=read_int(),v=read_int(); | ||
| + | T::Insert(u,v); | ||
| + | } | ||
| + | T::dfs_1(rt,0,0);T::dfs_2(rt,1); | ||
| + | T::build(1,1,n); | ||
| + | while(q--){ | ||
| + | int opt=read_int(); | ||
| + | if(opt==1){ | ||
| + | int u=read_int(),v=read_int(); | ||
| + | T::update_path(u,v,read_int()); | ||
| + | } | ||
| + | else{ | ||
| + | int k=read_int(); | ||
| + | _for(i,0,k){ | ||
| + | temp[i]=node[i]=read_int(); | ||
| + | is_key[node[i]]=true; | ||
| + | } | ||
| + | build(k); | ||
| + | tot_sz=0;root_sz=MAXN; | ||
| + | dfs_3(rt,0); | ||
| + | find_root(rt,0); | ||
| + | solve(root); | ||
| + | _for(i,0,k) | ||
| + | space(ans[temp[i]]); | ||
| + | putchar('\n'); | ||
| + | dfs_4(rt,0); | ||
| + | } | ||
| + | } | ||
| + | return 0; | ||
| + | } | ||
| + | </code> | ||
| + | </hidden> | ||