最近认真学了一下树剖(重链剖分),这里简单记录一些心得
目录
很多情况下我们需要在树上处理一些路径问题,比如叶子到根节点的路径最大子段和之类的,有时候还要加入修改操作,这时候树剖就是一个非常强有力的武器。对于每一个节点a,定义重儿子为其儿子节点中对应子树最大的儿子,重链为以a为起点的一条链,链中的每一个节点都是一个相应节点的重儿子。如图,红色为重儿子,蓝色路径为重链(还是很好理解的)当然,对于叶子节点来说,它自己本身就是一条重链。

如此来看,每一个节点都会属于一条重链,那么我们就可以在许多条链上来处理问题了。对于路径相关的问题,我们就可以在链之间切换。
如何每一个节点对应的重链?递归找它的重儿子以及重儿子的重儿子等即可。
如何找重儿子?dfs一遍并记录最大的子树大小即可。
这里我们还顺便记录了深度dep【】
其中son【】就是代表每一个节点的重儿子,没有重儿子的话,son自然记为0
fa【】代表父亲节点,siz【】代表子树大小(显然)
- void init_dfs(ll id,ll p,ll depth)
- {
- dep[id]=depth;
- siz[id]=1;
- fa[id]=p;
- ll son_val=0;
- for(int i=head[id];i!=-1;i=edge[i].next)
- {
- ll y=edge[i].t;
- if(y==p) continue;
- init_dfs(y,id,depth+1);
- siz[id]+=siz[y];
- if(siz[y]>son_val)
- {
- son_val=siz[y];
- son[id]=y;
- }
- }
- }
为了快速在重链之间跳转,我们需要记录每一个节点所在重链的起点,记为top【】,很明显重链的top是能不断向下传递记录的,对于轻儿子,它自己是一条重链的起点,所以从它开始top要改变。这里的idx数组记录的是每一个节点的dfn序,因为我们最后是要解决线性问题(路径),那么给他们一个编号就会方便很多。 此外,为了保证重链节点的dfn序的连续性,我们会优先递归重儿子。
- void dfs2(ll id,ll p)//节点编号,对应重链的起点
- {
- top[id]=p;
- idx[id]=++cnt;
- a[cnt]=mas[id];
- if(!son[id]) return;//如果没有重儿子就退出
- dfs2(son[id],p);//先递归重儿子,top为p
- for(int i=head[id];i!=-1;i=edge[i].next)
- {
- ll y=edge[i].t;
- if(!idx[y]) dfs2(y,y);//自己是重链的起点,top记为自己
- }
- }
到这里其实就没了,我们就已经成功地把这棵树剖成一条一条链了。
但是为了处理各种各样的区间问题,我们还会引入线段树。利用上文的idx数组,我们发现可以成功地把树上路径转为一维区间,那么就可以维护很多东西了,并且线段树构造与一般的并无区别。这里就不贴了。
还有问题就是查询以及修改。
如果是路径修改/查询的话:
我们自然就是在路径经过的一条条重链上进行处理。

对于每一条需要操作的重链,它是一段连续区间,这是线段树可以解决的问题,那么我们的思路就是让两个点不断跳重链并更新路径上的信息,最后来到同一条重链(top【】值相同)时再修改/查询一次就可以了。
如果是子树修改/查询的话:
子树的dfn序是连续的,所以这就是一个简单的线段树操作(大部分情况下);
下面以求树上路径和为例给出模板
- ll tree_sum(ll x,ll y)//树上路径和
- {
- ll ans=0;
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - ans=(ans+sum(1,idx[top[x]],idx[x]))%mod;
- x=fa[top[x]];
- }
- if(dep[x]>dep[y]) swap(x,y);
- //来到同一重链
- ans=(ans+sum(1,idx[x],idx[y]))%mod;
- return ans;
- }
-
- void tree_add(ll x,ll y,ll val)
- {
-
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - add(1,idx[top[x]],idx[x],val);
- x=fa[top[x]];
- }
- if(dep[x]>dep[y]) swap(x,y);
- //来到同一重链
- add(1,idx[x],idx[y],val);
- }
-
- void trson_add(ll x,ll val)//以x为根的子树所有权值+=val
- {
- add(1,idx[x],idx[x]+siz[x]-1,val%mod);
- }
-
- ll trson_sum(ll x)
- {
- return sum(1,idx[x],idx[x]+siz[x]-1);
- }
这就是树剖基础。
大意:
唯一的难处就是换根操作,但其实这个跟普通的换根dp的思想也是一样的,无非就是考虑根改变了之后对其它查询/操作的影响。
首先,换根对路径操作肯定是没有影响的(显然)
考虑其对子树操作的影响:
令root为当前根,x为要求的根,lca为两者的lca
1.u==root:直接输出整棵树的和就可以了
2.lca!=x:x的子树没有变,就正常查询就可以了
3.lca==x:x的子树就变成了x到root路径上第一个儿子的子树的补集(想一想),这时我们找到这个儿子,并求出它的子树和,再用全局和减去它即可。
求lca:
- ll get_lca(ll x,ll y)//获取两节点的lca
- {
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - x=fa[top[x]];
- }
- return dep[x]>dep[y]?y:x;
- }
求路径上的第一个儿子:
- ll find_firson(ll x,ll y)//找到x到y路径上的第一个儿子
- {
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - if(fa[top[x]]==y) return top[x];
- x=fa[top[x]];
- }
- if(dep[x]
swap(x,y); - return son[y];
- }
code:
- #include
- using namespace std;
- #define ll long long
- #define endl '\n'
- const ll N=1e5+10;
- inline int read()
- {
- int X=0; bool flag=1; char ch=getchar();
- while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
- while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
- if(flag) return X;
- return ~(X-1);
- }
- ll n,m,rt,fir,sec,op,x,y,z;
- struct ty{
- ll t,l,next;
- }edge[N<<1];
- struct tree
- {
- ll l,r;
- ll val;
- ll add;//lazy
- ll siz;
- }tr[N<<2];
- ll cn=0;
- ll head[N];
- void add(ll a,ll b,ll c)
- {
- edge[++cn].t=b;
- edge[cn].l=c;
- edge[cn].next=head[a];
- head[a]=cn;
- }
-
- ll dep[N];//节点深度
- ll fa[N];//节点父亲
- ll son[N];//节点重儿子
- ll siz[N];//节点子树的大小
- ll mas[N],a[N];//初始序列 处理后的序列
- ll cnt=0;//dfs序的计数器
- ll idx[N];//节点的dfs序 id
- ll top[N];//节点所在链的顶端 要在init_dfs处理之后才能更新这个
-
- void init_dfs(ll id,ll p,ll depth)//节点 父亲 深度
- {//更新重儿子以及各节点的子树大小&深度
- dep[id]=depth;
- fa[id]=p;
- siz[id]=1;
- ll son_val=0;//重儿子的子树大小
- for(int i=head[id];i!=-1;i=edge[i].next)
- {
- ll y=edge[i].t;
- if(y==p) continue;
- init_dfs(y,id,depth+1);
- siz[id]+=siz[y];//子树大小更新
- if(siz[y]>son_val)
- {
- son_val=siz[y];
- son[id]=y;
- }
- }
- }
-
- void dfs2(ll id,ll p)
- {
- idx[id]=++cnt;
- a[cnt]=mas[id];//序列转移
- top[id]=p;
- if(!son[id]) return;//无重儿子的情况(也就是无儿子,是叶子节点)直接返回
- //下面的dfs顺序:先遍历重儿子,再遍历轻儿子
- dfs2(son[id],p);//顶端idx不变
- for(int i=head[id];i!=-1;i=edge[i].next)
- {
- ll y=edge[i].t;
- if(!idx[y]) dfs2(y,y);//新的链(轻链)
- }
- }
-
- void push_up(ll p)
- {
- tr[p].val=(tr[p<<1].val+tr[p<<1|1].val);
- }
-
- void build(ll p,ll l,ll r)
- {
- tr[p].l=l;tr[p].r=r;tr[p].siz=r-l+1;
- if(l==r)
- {
- tr[p].val=a[l];//用新的序列值
- return;
- }
- ll mid=l+r>>1;
- build(p<<1,l,mid);
- build(p<<1|1,mid+1,r);
- push_up(p);
- }
-
- void push_down(ll p)
- {
- if(tr[p].add==0) return;
- tr[p<<1].val=(tr[p<<1].val+tr[p<<1].siz*tr[p].add);
- tr[p<<1|1].val=(tr[p<<1|1].val+tr[p<<1|1].siz*tr[p].add);
- tr[p<<1].add=(tr[p<<1].add+tr[p].add);
- tr[p<<1|1].add=(tr[p<<1|1].add+tr[p].add);
- tr[p].add=0;//下传结束
- }
-
- void add(ll p,ll l,ll r,ll val)
- {
- if(tr[p].l>=l&&tr[p].r<=r)
- {
- tr[p].val=(tr[p].val+tr[p].siz*val);
- tr[p].add=(tr[p].add+val);
- return;
- }
- push_down(p);
- ll mid=tr[p].l+tr[p].r>>1;
- if(l<=mid) add(p<<1,l,r,val);
- if(r>=mid+1) add(p<<1|1,l,r,val);
- push_up(p);
- }
-
- ll sum(ll p,ll l,ll r)
- {
- ll ans=0;
- if(tr[p].l>=l&&tr[p].r<=r) return tr[p].val;
- push_down(p);
- ll mid=tr[p].l+tr[p].r>>1;
- if(l<=mid) ans=(ans+sum(p<<1,l,r));
- if(r>=mid+1) ans=(ans+sum(p<<1|1,l,r));
- return ans;
- }
-
- ll tree_sum(ll x,ll y)//树上路径和
- {
- ll ans=0;
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - ans=(ans+sum(1,idx[top[x]],idx[x]));
- x=fa[top[x]];
- }
- if(dep[x]>dep[y]) swap(x,y);
- //来到同一重链
- ans=(ans+sum(1,idx[x],idx[y]));
- return ans;
- }
-
- void tree_add(ll x,ll y,ll val)
- {
-
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - add(1,idx[top[x]],idx[x],val);
- x=fa[top[x]];
- }
- if(dep[x]>dep[y]) swap(x,y);
- //来到同一重链
- add(1,idx[x],idx[y],val);
- }
-
- ll get_lca(ll x,ll y)//获取两节点的lca
- {
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - x=fa[top[x]];
- }
- return dep[x]>dep[y]?y:x;
- }
-
- ll find_firson(ll x,ll y)//找到x到y路径上的第一个儿子
- {
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - if(fa[top[x]]==y) return top[x];
- x=fa[top[x]];
- }
- if(dep[x]
swap(x,y); - return son[y];
- }
-
- void trson_add(ll x,ll val)//以x为根的子树所有权值+=val
- {
- if(x==rt)
- {
- add(1,1,n,val);
- return;
- }
- //
- ll LCA=get_lca(x,rt);
- if(LCA!=x)
- {
- add(1,idx[x],idx[x]+siz[x]-1,val);
- return;
- }
- //
- ll child=find_firson(x,rt);
- add(1,1,n,val);
- add(1,idx[child],idx[child]+siz[child]-1,-val);
-
- }
-
- ll trson_sum(ll x)
- {
- if(x==rt) return sum(1,1,n);
- ///
- ll LCA=get_lca(x,rt);
- if(LCA!=x) return sum(1,idx[x],idx[x]+siz[x]-1);
- ///
- ll child=find_firson(x,rt);
- ll res=sum(1,1,n);//整棵树的总和
- ll f_res=sum(1,idx[child],idx[child]+siz[child]-1);
- return res-f_res;
- }
-
-
- int main()
- {
- memset(head,-1,sizeof head);
- n=read();
- rt=1;
- for(int i=1;i<=n;++i) mas[i]=read();
- for(int i=1;i
- {
- fir=read();
- add(fir,i+1,1);
- add(i+1,fir,1);
- }
- init_dfs(rt,0,1);
- dfs2(rt,rt);
- build(1,1,n);
- m=read();
- while(m--)
- {
- op=read();
- if(op==2)
- {
- x=read();y=read();z=read();
- tree_add(x,y,z);
- continue;
- }
- else if(op==4)
- {
- x=read();y=read();
- printf("%lld\n",tree_sum(x,y));
- continue;
- }
- else if(op==3)
- {
-
- x=read();z=read();
- trson_add(x,z);
- continue;
- }
- else if(op==5)
- {
- x=read();
- printf("%lld\n",trson_sum(x));
- continue;
- }
- else if(op==1)
- {
- x=read();
- rt=x;
- }
- }
- return 0;
- }
大意:
操作1:路径总体权值+1
操作2:查询路径最大值
思路:
很显然也是板子,线段树维护区间最大值即可
- #include
- using namespace std;
- #define ll int
- #define endl '\n'
- const ll N=5e4+10;
- struct ty{
- ll t,l,next;
- }edge[N<<1];
- ll cn=0;
- ll n,k;
- ll aa,b,c,rt;
- ll head[N];
- void add_edge(ll a,ll b,ll c)
- {
- edge[++cn].t=b;
- edge[cn].l=c;
- edge[cn].next=head[a];
- head[a]=cn;
- }
- struct tree
- {
- ll l,r;
- ll val;
- ll add;//lazy
- ll siz;
- }tr[N<<2];
- ll dep[N];//深度
- ll fa[N],son[N];
- ll siz[N];
- ll mas[N],a[N];//初始序列,处理后的序列
- ll cnt=0;//dfs序的时间戳
- ll top[N];//所在链的顶端
- ll idx[N];
- void init_dfs(ll id,ll p,ll depth)
- {
- dep[id]=depth;
- fa[id]=p;
- siz[id]=1;
- ll son_val=0;
- for(int i=head[id];i!=-1;i=edge[i].next)
- {
- ll y=edge[i].t;
- if(y==p) continue;
- init_dfs(y,id,depth+1);
- siz[id]+=siz[y];
- if(siz[y]>son_val)
- {
- son_val=siz[y];
- son[id]=y;
- }
- }
- }
- void dfs2(ll id,ll p)
- {//更新链上的情况
- idx[id]=++cnt;
- a[cnt]=mas[id];
- top[id]=p;
- if(!son[id]) return;
- dfs2(son[id],p);
- for(int i=head[id];i!=-1;i=edge[i].next)
- {
- ll y=edge[i].t;
- //if(y==p||y==son[id]) continue;
- if(!idx[y]) dfs2(y,y);//新的链
- }
- }
- void push_up(ll p)
- {
- tr[p].val=max(tr[p<<1].val,tr[p<<1|1].val);
- }
- void build(ll p,ll l,ll r)
- {
- tr[p].l=l;tr[p].r=r;tr[p].siz=r-l+1;
- if(l==r)
- {
- tr[p].val=a[l];
- return;
- }
- ll mid=l+r>>1;
- build(p<<1,l,mid);
- build(p<<1|1,mid+1,r);
- }
- void push_down(ll p)
- {
- if(tr[p].add==0) return;
- tr[p<<1].val+=tr[p].add;
- tr[p<<1|1].val+=tr[p].add;
- tr[p<<1].add+=tr[p].add;
- tr[p<<1|1].add+=tr[p].add;
- tr[p].add=0;
- }
- void add(ll p,ll l,ll r,ll val)
- {
- if(tr[p].l>=l&&tr[p].r<=r)
- {
- tr[p].val+=val;
- tr[p].add+=val;
- return;
- }
- push_down(p);
- ll mid=tr[p].l+tr[p].r>>1;
- if(l<=mid) add(p<<1,l,r,val);
- if(r>mid) add(p<<1|1,l,r,val);
- push_up(p);
- }
- ll sum(ll p,ll l,ll r)
- {
- ll ans=0;
- if(tr[p].l>=l&&tr[p].r<=r)
- {
- return tr[p].val;
- }
- push_down(p);
- ll mid=tr[p].l+tr[p].r>>1;
- if(l<=mid) ans=max(ans,sum(p<<1,l,r));
- if(r>mid) ans=max(ans,sum(p<<1|1,l,r));
- return ans;
- }
- ll tree_sum(ll x,ll y)
- {
- ll ans=0;
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - ans=max(ans,sum(1,idx[top[x]],idx[x]));
- x=fa[top[x]];
- }
- if(dep[x]
swap(x,y); - ans=max(ans,sum(1,idx[x],idx[y]));
- return ans;
- }
- ll tree_add(ll x,ll y,ll val)
- {
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - add(1,idx[top[x]],idx[x],val);
- x=fa[top[x]];
- }
- if(dep[x]>dep[y]) swap(x,y);
- add(1,idx[x],idx[y],val);
- }
- void trson_add(ll x,ll val)
- {
- add(1,idx[x],idx[x]+siz[x]-1,val);
- }
- ll trson_sum(ll x)
- {
- return sum(1,idx[x],idx[x]+siz[x]-1);
- }
- void solve()
- {
- cin>>n>>k;
- for(int i=1;i
- {
- cin>>aa>>b;
- add_edge(aa,b,1);
- add_edge(b,aa,1);
- }
- init_dfs(rt,0,1);
- dfs2(rt,rt);
- build(1,1,n);
- while(k--)
- {
- cin>>aa>>b;
- tree_add(aa,b,1);
- }
- cout<<sum(1,1,n);
- }
- int main()
- {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
- memset(head,-1,sizeof head);
- rt=1;
- solve();
- return 0;
- }
边权下放点权
大意:
路径上边权+1,查询两点之间的边权。
一个很明显的思路是边权下放点权,详见我的另一篇博客
大意:
单点修改,路径查询
思路:
水
- #include
- using namespace std;
- #define ll int
- #define endl '\n'
- const ll N=1e5+10;
- struct ty{
- ll t,l,next;
- }edge[N<<1];
- ll cn=0;
- ll head[N];
- ll n,q,rt;
- ll aa,b,op;
- void add_edge(ll a,ll b,ll c)
- {
- edge[++cn].l=c;
- edge[cn].t=b;
- edge[cn].next=head[a];
- head[a]=cn;
- }
- struct tree{
- ll l,r;
- ll val;
- ll add;
- ll siz;
- }tr[N<<2];
- ll dep[N];
- ll siz[N],son[N],fa[N];
- ll a[N],mas[N];
- ll cnt=0;//dfn时间戳
- ll idx[N];
- ll top[N];
- void init_dfs(ll id,ll p,ll depth)
- {
- dep[id]=depth;
- fa[id]=p;
- siz[id]=1;
- ll son_size=0;
- for(int i=head[id];i!=-1;i=edge[i].next)
- {
- ll y=edge[i].t;
- if(y==p) continue;
- init_dfs(y,id,depth+1);
- siz[id]+=siz[y];
- if(siz[y]>son_size)
- {
- son[id]=y;
- son_size=siz[y];
- }
- }
- }
- void dfs2(ll id,ll p)
- {
- idx[id]=++cnt;
- a[cnt]=mas[id];
- top[id]=p;
- if(!son[id]) return;
- dfs2(son[id],p);
- for(int i=head[id];i!=-1;i=edge[i].next)
- {
- ll y=edge[i].t;
- if(!idx[y]) dfs2(y,y);
- }
- }
- void push_up(ll p)
- {
- tr[p].val=(tr[p<<1].val^tr[p<<1|1].val);
- }
- void build(ll p,ll l,ll r)
- {
- tr[p].l=l;tr[p].r=r;tr[p].siz=r-l+1;
- if(l==r)
- {
- tr[p].val=a[l];
- return;
- }
- ll mid=l+r>>1;
- build(p<<1,l,mid);
- build(p<<1|1,mid+1,r);
- push_up(p);
- }
- void add(ll p,ll l,ll r,ll val)
- {
- if(tr[p].l==tr[p].r)
- {
- tr[p].val=val;
- return;
- }
- ll mid=tr[p].l+tr[p].r>>1;
- if(l<=mid) add(p<<1,l,r,val);
- else add(p<<1|1,l,r,val);
- push_up(p);
- }
- ll sum(ll p,ll l,ll r)
- {
- if(tr[p].l>=l&&tr[p].r<=r)
- {
- return tr[p].val;
- }
- ll mid=tr[p].l+tr[p].r>>1;
- ll ans=0;
- if(l<=mid) ans^=sum(p<<1,l,r);
- if(r>mid) ans^=sum(p<<1|1,l,r);
- return ans;
- }
- void tree_add(ll x,ll y,ll val)
- {
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - add(1,idx[top[x]],idx[x],val);
- x=fa[top[x]];
- }
- if(dep[x]>dep[y]) swap(x,y);
- add(1,idx[x],idx[y],val);
- }
- ll tree_sum(ll x,ll y)
- {
- ll ans=0;
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - ans^=sum(1,idx[top[x]],idx[x]);
- x=fa[top[x]];
- }
- if(dep[x]>dep[y]) swap(x,y);
- ans^=sum(1,idx[x],idx[y]);
- return ans;
- }
- int main()
- {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
- memset(head,-1,sizeof head);
- rt=1;
- cin>>n>>q;
- for(int i=1;i<=n;++i) cin>>mas[i];
- for(int i=1;i
- {
- cin>>aa>>b;
- add_edge(aa,b,1);
- add_edge(b,aa,1);
- }
- init_dfs(rt,0,1);
- dfs2(rt,rt);
- build(1,1,n);
- while(q--)
- {
- cin>>op>>aa>>b;
- if(op==1)
- {
- tree_add(aa,aa,b);
- }
- else cout<<tree_sum(aa,b)<
- }
- return 0;
- }
大意:

思路:
要求祖先最近,也就是求该点到根的路径上深度最深的被标记过的点。
那么线段树记录被标记过的点的dfn序,然后维护区间最大值即可。注意处置记为-1
- #include
- using namespace std;
- #define ll int
- #define endl '\n'
- const ll N=1e5+10;
- struct ty{
- ll t,l,next;
- }edge[N<<1];
- ll cn=0;
- ll head[N];
- ll n,q,rt;
- ll aa,b;
- ll sd[N];
- char op;
- void add_edge(ll a,ll b,ll c)
- {
- edge[++cn].l=c;
- edge[cn].t=b;
- edge[cn].next=head[a];
- head[a]=cn;
- }
- struct tree{
- ll l,r;
- ll val;
- ll add;
- ll siz;
- }tr[N<<2];
- ll dep[N];
- ll siz[N],son[N],fa[N];
- ll a[N],mas[N];
- ll cnt=0;//dfn时间戳
- ll idx[N];
- ll top[N];
- void init_dfs(ll id,ll p,ll depth)
- {
- dep[id]=depth;
- fa[id]=p;
- siz[id]=1;
- ll son_size=0;
- for(int i=head[id];i!=-1;i=edge[i].next)
- {
- ll y=edge[i].t;
- if(y==p) continue;
- init_dfs(y,id,depth+1);
- siz[id]+=siz[y];
- if(siz[y]>son_size)
- {
- son[id]=y;
- son_size=siz[y];
- }
- }
- }
- void dfs2(ll id,ll p)
- {
- idx[id]=++cnt;
- sd[cnt]=id;
- a[cnt]=mas[id];
- top[id]=p;
- if(!son[id]) return;
- dfs2(son[id],p);
- for(int i=head[id];i!=-1;i=edge[i].next)
- {
- ll y=edge[i].t;
- if(!idx[y]) dfs2(y,y);
- }
- }
- void push_up(ll p)
- {
- tr[p].val=max(tr[p<<1].val,tr[p<<1|1].val);
- }
- void build(ll p,ll l,ll r)
- {
- tr[p].l=l;tr[p].r=r;tr[p].siz=r-l+1;
- if(l==r)
- {
- tr[p].val=-1;
- return;
- }
- ll mid=l+r>>1;
- build(p<<1,l,mid);
- build(p<<1|1,mid+1,r);
- push_up(p);
- }
- void add(ll p,ll l,ll r,ll val)
- {
- if(tr[p].l==tr[p].r)
- {
- if(val) tr[p].val=tr[p].l;
- return;
- }
- ll mid=tr[p].l+tr[p].r>>1;
- if(l<=mid) add(p<<1,l,r,val);
- else add(p<<1|1,l,r,val);
- push_up(p);
- }
- ll sum(ll p,ll l,ll r)
- {
- if(tr[p].l>=l&&tr[p].r<=r)
- {
- return tr[p].val;
- }
- ll mid=tr[p].l+tr[p].r>>1;
- ll ans=-1;
- if(l<=mid) ans=max(ans,sum(p<<1,l,r));
- if(r>mid) ans=max(ans,sum(p<<1|1,l,r));
- return ans;
- }
- void tree_add(ll x,ll y,ll val)
- {
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - add(1,idx[top[x]],idx[x],val);
- x=fa[top[x]];
- }
- if(dep[x]>dep[y]) swap(x,y);
- add(1,idx[x],idx[y],val);
- }
- ll tree_sum(ll x,ll y)
- {
- ll ans=-1;
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - ans=sum(1,idx[top[x]],idx[x]);
- if(ans!=-1) return sd[ans];
- x=fa[top[x]];
- }
- if(dep[x]>dep[y]) swap(x,y);
- ans=sum(1,idx[x],idx[y]);
- return sd[ans];
- }
- int main()
- {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
- memset(head,-1,sizeof head);
- rt=1;
- cin>>n>>q;
- //for(int i=1;i<=n;++i) cin>>mas[i];
- for(int i=1;i
- {
- cin>>aa>>b;
- add_edge(aa,b,1);
- add_edge(b,aa,1);
- }
- init_dfs(rt,0,1);
- dfs2(rt,rt);
- build(1,1,n);
- add(1,1,1,1);
- while(q--)
- {
- cin>>op>>aa;
- if(op=='C')
- {
- add(1,idx[aa],idx[aa]+1,1);
- // tree_add(aa,aa,1);
- }
- else cout<<tree_sum(aa,1)<
- }
- return 0;
- }
区间覆盖
大意:

操作比较多,有三个修改操作,对于第二/三个操作,我们记录add2,add1(别问我为什么是反的,写的时候脑子丢了吧...).pushdown时很明显先处理add2,因为它代表区间替换,那么之前有没有加的操作并不重要,当然得把add1儿子的add1清零。然后再加上边权与点权的转换就可以了。代码还是有点长的,写起来很酸爽。
- #include
- using namespace std;
- #define ll long long
- #define endl '\n'
- const ll N=1e5+10;
- struct ty
- {
- ll t,l,next;
- }edge[N<<1];
- struct tree
- {
- ll l,r;
- ll val;
- ll add1,add2=-1;//加,替换
- ll siz;
- }tr[N<<2];
- ll cn=0;
- ll head[N];
- void add_edge(ll a,ll b,ll c)
- {
- edge[++cn].t=b;
- edge[cn].l=c;
- edge[cn].next=head[a];
- head[a]=cn;
- }
- ll siz[N],fa[N],son[N],dep[N],idx[N],a[N],mas[N],top[N];
- ll n,m,cnt,rt,fir,sec,thi,x,y,z;
- pair
church[N]; - string op;
- void init_dfs(ll id,ll p,ll depth)
- {
- siz[id]=1;
- dep[id]=depth;
- fa[id]=p;
- ll son_val=0;
- for(int i=head[id];i!=-1;i=edge[i].next)
- {
- ll y=edge[i].t;
- if(y==p) continue;
- mas[y]=edge[i].l;
- init_dfs(y,id,depth+1);
- siz[id]+=siz[y];
- if(siz[y]>son_val)
- {
- son_val=siz[y];
- son[id]=y;
- }
- }
- }
- void dfs2(ll id,ll p)
- {
- top[id]=p;
- idx[id]=++cnt;
- a[cnt]=mas[id];
- if(!son[id]) return;
- dfs2(son[id],p);
- for(int i=head[id];i!=-1;i=edge[i].next)
- {
- ll y=edge[i].t;
- if(!idx[y]) dfs2(y,y);
- }
- }
- void push_up(ll p)
- {
- tr[p].val=max(tr[p<<1].val,tr[p<<1|1].val);
- }
- void push_down(ll p)
- {
- if(tr[p].add2>=0)
- {
- tr[p<<1].add1=tr[p<<1|1].add1=0;
- tr[p<<1].val=tr[p<<1|1].val=tr[p].add2;
- tr[p<<1].add2=tr[p<<1|1].add2=tr[p].add2;
- tr[p].add2=-1;
- }
- if(tr[p].add1)
- {
- tr[p<<1].add1+=tr[p].add1;
- tr[p<<1|1].add1+=tr[p].add1;
- tr[p<<1].val+=tr[p].add1;
- tr[p<<1|1].val+=tr[p].add1;
- tr[p].add1=0;
- }
- }
- void build(ll p,ll l,ll r)
- {
- tr[p].l=l;tr[p].r=r;
- tr[p].siz=r-l+1;
- if(l==r)
- {
- tr[p].val=a[l];
- tr[p].add1=0;tr[p].add2=-1;
- return;
- }
- ll mid=r+l>>1;
- build(p<<1,l,mid);
- build(p<<1|1,mid+1,r);
- push_up(p);
- }
- void ti_huan(ll p,ll l,ll r,ll val)//cover
- {
- if(tr[p].l>=l&&tr[p].r<=r)
- {
- tr[p].val=val;
- tr[p].add1=0;tr[p].add2=val;
- return;
- }
- push_down(p);
- ll mid=tr[p].l+tr[p].r>>1;
- if(l<=mid) ti_huan(p<<1,l,r,val);
- if(r>mid) ti_huan(p<<1|1,l,r,val);
- push_up(p);
- }
- void add(ll p,ll l,ll r,ll val)
- {
- if(tr[p].l>=l&&tr[p].r<=r)
- {
- tr[p].val+=val;
- tr[p].add1+=val;
- return;
- }
- push_down(p);
- ll mid=tr[p].l+tr[p].r>>1;
- if(l<=mid) add(p<<1,l,r,val);
- if(r>mid) add(p<<1|1,l,r,val);
- push_up(p);
- }
- ll sum(ll p,ll l,ll r)
- {
- if(tr[p].l>=l&&tr[p].r<=r)
- {
- return tr[p].val;
- }
- ll ans=0;
- push_down(p);
- ll mid=tr[p].l+tr[p].r>>1;
- if(l<=mid) ans=max(ans,sum(p<<1,l,r));
- if(r>mid) ans=max(ans,sum(p<<1|1,l,r));
- //cout<<"SUM "<
- return ans;
- }
- void change(ll p,ll l,ll val)
- {
- if(tr[p].l==tr[p].r)
- {
- tr[p].add1=0;
- tr[p].add2=val;
- tr[p].val=val;
- return;
- }
- push_down(p);
- ll mid=tr[p].l+tr[p].r>>1;
- if(l<=mid) change(p<<1,l,val);
- else change(p<<1|1,l,val);
- push_up(p);
- }
- void tree_cover(ll x,ll y,ll val)
- {
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - ti_huan(1,idx[top[x]],idx[x],val);
- x=fa[top[x]];
- }
- if(dep[x]>dep[y]) swap(x,y);
- ti_huan(1,idx[x]+1,idx[y],val);
- }
- void tree_add(ll x,ll y,ll val)
- {
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - add(1,idx[top[x]],idx[x],val);
- x=fa[top[x]];
- }
- if(dep[x]>dep[y]) swap(x,y);
- add(1,idx[x]+1,idx[y],val);
- }
- ll tree_sum(ll x,ll y)
- {
- ll ans=0;
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - ans=max(ans,sum(1,idx[top[x]],idx[x]));
- x=fa[top[x]];
- }
- if(dep[x]>dep[y]) swap(x,y);
- ans=max(ans,sum(1,idx[x]+1,idx[y]));
- return ans;
- }
- int main()
- {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
- memset(head,-1,sizeof head);
- cin>>n;
- for(int i=1;i
- {
- cin>>fir>>sec>>thi;
- add_edge(fir,sec,thi);
- add_edge(sec,fir,thi);
- church[i]=make_pair(fir,sec);
- }
- rt=1;
- init_dfs(rt,0,1);
- dfs2(rt,rt);
- build(1,1,n);
- // for(int i=1;i<=n;++i) cout<
- while(cin>>op)
- {
- if(op[0]=='S') break;
- if(op=="Max")
- {
- cin>>x>>y;
- cout<<tree_sum(x,y)<
- continue;
- }
- if(op=="Cover")
- {
- cin>>x>>y>>z;
- tree_cover(x,y,z);
- continue;
- }
- if(op=="Add")
- {
- cin>>x>>y>>z;
- tree_add(x,y,z);
- continue;
- }
- if(op=="Change")
- {
- cin>>x>>y;
- ll sd;
- if(dep[church[x].first]>dep[church[x].second]) sd=church[x].first;
- else sd=church[x].second;
- change(1,idx[sd],y);
- }
- }
- return 0;
- }
大意:

思路:
基操,只是维护了两个值而已。
- #include
- using namespace std;
- #define ll long long
- #define endl '\n'
- const ll N=2e5+10;
- struct ty{
- ll t,l,next;
- }edge[N<<1];
- ll head[N];
- ll cn=0;
- void add_edge(ll a,ll b,ll c)
- {
- edge[++cn].t=b;
- edge[cn].l=c;
- edge[cn].next=head[a];
- head[a]=cn;
- }
- struct tree
- {
- ll l,r;
- ll val;
- ll maxn;
- ll add;
- ll siz;
- }tr[N<<2];
- ll siz[N],fa[N],idx[N],a[N],mas[N],top[N],son[N],dep[N];
- ll n,m,rt,fir,sec,x,y,z,cnt;
- string op;
- void init_dfs(ll id,ll p,ll depth)
- {
- fa[id]=p;
- dep[id]=depth;
- siz[id]=1;
- ll son_val=0;
- for(int i=head[id];i!=-1;i=edge[i].next)
- {
- ll y=edge[i].t;
- if(y==p) continue;
- init_dfs(y,id,depth+1);
- siz[id]+=siz[y];
- if(siz[y]>=son_val)
- {
- son_val=siz[y];
- son[id]=y;
- }
- }
- }
- void dfs2(ll id,ll p)
- {
- top[id]=p;
- idx[id]=++cnt;
- a[cnt]=mas[id];
- if(!son[id]) return;
- dfs2(son[id],p);
- for(int i=head[id];i!=-1;i=edge[i].next)
- {
- ll y=edge[i].t;
- if(!idx[y]) dfs2(y,y);
- }
- }
- void push_up(ll p)
- {
- tr[p].maxn=max(tr[p<<1].maxn,tr[p<<1|1].maxn);
- tr[p].val=tr[p<<1].val+tr[p<<1|1].val;
- }
- void build(ll p,ll l,ll r)
- {
- tr[p].l=l;tr[p].r=r;tr[p].siz=r-l+1;
- if(l==r)
- {
- tr[p].maxn=tr[p].val=a[l];
- return;
- }
- ll mid=l+r>>1;
- build(p<<1,l,mid);
- build(p<<1|1,mid+1,r);
- push_up(p);
- }
- void change(ll p,ll l,ll val)
- {
- if(tr[p].l==tr[p].r)
- {
- tr[p].val=tr[p].maxn=val;
- return;
- }
- ll mid=tr[p].l+tr[p].r>>1;
- if(l<=mid) change(p<<1,l,val);
- else change(p<<1|1,l,val);
- push_up(p);
- }
- ll sum(ll p,ll l,ll r)
- {
- if(tr[p].l>=l&&tr[p].r<=r)
- {
- return tr[p].val;
- }
- ll mid=tr[p].l+tr[p].r>>1;
- ll ans=0;
- if(l<=mid) ans+=sum(p<<1,l,r);
- if(r>mid) ans+=sum(p<<1|1,l,r);
- return ans;
- }
- ll maxn(ll p,ll l,ll r)
- {
- if(tr[p].l>=l&&tr[p].r<=r)
- {
- return tr[p].maxn;
- }
- ll mid=tr[p].l+tr[p].r>>1;
- ll ans=-1e15;
- if(l<=mid) ans=max(ans,maxn(p<<1,l,r));
- if(r>mid) ans=max(ans,maxn(p<<1|1,l,r));
- return ans;
- }
- ll tree_sum(ll x,ll y)
- {
- ll ans=0;
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - ans+=sum(1,idx[top[x]],idx[x]);
- x=fa[top[x]];
- }
- if(dep[x]>dep[y]) swap(x,y);
- ans+=sum(1,idx[x],idx[y]);
- return ans;
- }
- ll tree_maxn(ll x,ll y)
- {
- ll ans=-1e15;
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - ans=max(ans,maxn(1,idx[top[x]],idx[x]));
- x=fa[top[x]];
- }
- if(dep[x]>dep[y]) swap(x,y);
- ans=max(ans,maxn(1,idx[x],idx[y]));
- return ans;
- }
- int main()
- {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
- memset(head,-1,sizeof head);
- cin>>n;
- for(int i=1;i
- {
- cin>>fir>>sec;
- add_edge(fir,sec,1);
- add_edge(sec,fir,1);
- }
- for(int i=1;i<=n;++i) cin>>mas[i];
- cin>>m;
- rt=1;
- init_dfs(rt,0,1);
- dfs2(rt,rt);
- build(1,1,n);
- for(int i=1;i<=m;++i)
- {
- cin>>op;
- cin>>x>>y;
- if(op[0]=='C')
- {
- change(1,idx[x],y);
- continue;
- }
- else if(op[1]=='M')
- {
- cout<<tree_maxn(x,y)<
- }
- else cout<<tree_sum(x,y)<
- }
- return 0;
- }
大意:
区间加,子树查询
思路:
还是板子
- #include
- using namespace std;
- #define ll long long
- #define endl '\n'
- const ll N=2e5+10;
- struct ty
- {
- ll t,l,next;
- }edge[N<<1];
- ll head[N];
- ll cn=0;
- void add_edge(ll a,ll b,ll c)
- {
- edge[++cn].t=b;
- edge[cn].next=head[a];
- edge[cn].l=c;
- head[a]=cn;
- }
- struct tree
- {
- ll l,r;
- ll val;
- ll add;
- ll siz;
- }tr[N<<2];
- ll son[N],fa[N],siz[N],top[N],dep[N],idx[N],a[N],mas[N];
- ll cnt=0,n,m,rt,fir,sec,x,y,z;
- char op;
- void init_dfs(ll id,ll p,ll depth)
- {
- dep[id]=depth;
- fa[id]=p;
- siz[id]=1;
- ll son_val=0;
- for(int i=head[id];i!=-1;i=edge[i].next)
- {
- ll y=edge[i].t;
- if(y==p) continue;
- init_dfs(y,id,depth+1);
- siz[id]+=siz[y];
- if(siz[y]>son_val)
- {
- son_val=siz[y];
- son[id]=y;
- }
- }
- }
- void dfs2(ll id,ll p)
- {
- top[id]=p;
- idx[id]=++cnt;
- a[cnt]=mas[id];
- if(!son[id]) return;
- dfs2(son[id],p);
- for(int i=head[id];i!=-1;i=edge[i].next)
- {
- ll y=edge[i].t;
- if(!idx[y]) dfs2(y,y);
- }
- }
- void push_up(ll p)
- {
- tr[p].val=tr[p<<1].val+tr[p<<1|1].val;
- }
- void push_down(ll p)
- {
- if(tr[p].add==0) return;
- tr[p<<1].val+=tr[p<<1].siz*tr[p].add;
- tr[p<<1|1].val+=tr[p<<1|1].siz*tr[p].add;
- tr[p<<1].add+=tr[p].add;
- tr[p<<1|1].add+=tr[p].add;
- tr[p].add=0;
- }
- void build(ll p,ll l,ll r)
- {
- tr[p].l=l;tr[p].r=r;tr[p].siz=r-l+1;
- if(l==r)
- {
- tr[p].val=a[l];
- return;
- }
- ll mid=l+r>>1;
- build(p<<1,l,mid);
- build(p<<1|1,mid+1,r);
- push_up(p);
- }
- void add(ll p,ll l,ll r,ll val)
- {
- if(tr[p].l>=l&&tr[p].r<=r)
- {
- tr[p].val+=tr[p].siz*val;
- tr[p].add+=val;
- return;
- }
- push_down(p);
- ll mid=tr[p].l+tr[p].r>>1;
- if(l<=mid) add(p<<1,l,r,val);
- if(r>mid) add(p<<1|1,l,r,val);
- push_up(p);
- }
- ll sum(ll p,ll l,ll r)
- {
- if(tr[p].l>=l&&tr[p].r<=r)
- {
- return tr[p].val;
- }
- push_down(p);
- ll mid=tr[p].l+tr[p].r>>1;
- ll ans=0;
- if(l<=mid) ans+=sum(p<<1,l,r);
- if(r>mid) ans+=sum(p<<1|1,l,r);
- return ans;
- }
- void tree_add(ll x,ll y,ll val)
- {
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - add(1,idx[top[x]],idx[x],val);
- x=fa[top[x]];
- }
- if(dep[x]>dep[y]) swap(x,y);
- add(1,idx[x],idx[y],val);
- }
- ll tree_sum(ll x,ll y)
- {
- ll ans=0;
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - ans+=sum(1,idx[top[x]],idx[x]);
- x=fa[top[x]];
- }
- if(dep[x]>dep[y]) swap(x,y);
- ans+=sum(1,idx[x],idx[y]);
- return ans;
- }
- ll trson_sum(ll x)
- {
- return sum(1,idx[x],idx[x]+siz[x]-1);
- }
- int main()
- {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
- memset(head,-1,sizeof head);
- cin>>n;
- for(int i=1;i
- {
- cin>>fir>>sec;
- fir++;sec++;
- add_edge(fir,sec,1);
- add_edge(sec,fir,1);
- }
- cin>>m;
- rt=1;
- init_dfs(rt,0,1);
- dfs2(rt,rt);
- build(1,1,n);
- while(m--)
- {
- cin>>op;
- if(op=='A')
- {
- cin>>x>>y>>z;
- x++;y++;
- tree_add(x,y,z);
- continue;
- }
- cin>>x;
- x++;
- cout<<trson_sum(x)<
- }
- return 0;
- }
大意:
install操作:查询节点到根的权值为0的个数,并区间置1
unstall操作:查询子树权值为1的个数,并区间置0
思路:
区间置操作是很简单的,没什么好讲
对于第一种操作,记录一下修改前的值,那么区间长度减去它就是答案了
对于第二种操作,直接返回区间和就可以了
- #include
- using namespace std;
- #define ll long long
- #define endl '\n'
- const ll N=1e5+10;
- struct ty
- {
- ll t,l,next;
- }edge[N<<1];
- ll cn=0;
- ll head[N];
- void add_edge(ll a,ll b,ll c)
- {
- edge[++cn].t=b;
- edge[cn].l=c;
- edge[cn].next=head[a];
- head[a]=cn;
- }
- struct tree
- {
- ll l,r,siz=0;
- ll add=0;
- ll val=0;
- }tr[N<<2];
- ll siz[N],dep[N],son[N],fa[N],idx[N],mas[N],a[N],top[N];
- ll n,m,fir,sec,cnt,x,y,z,rt;
- string op;
- void init_dfs(ll id,ll p,ll depth)
- {
- fa[id]=p;
- dep[id]=depth;
- siz[id]=1;
- ll son_val=0;
- for(int i=head[id];i!=-1;i=edge[i].next)
- {
- ll y=edge[i].t;
- if(y==p) continue;
- init_dfs(y,id,depth+1);
- siz[id]+=siz[y];
- if(siz[y]>son_val)
- {
- son_val=siz[y];
- son[id]=y;
- }
- }
- }
- void dfs2(ll id,ll p)
- {
- top[id]=p;
- idx[id]=++cnt;
- a[cnt]=mas[id];
- if(!son[id]) return;
- dfs2(son[id],p);
- for(int i=head[id];i!=-1;i=edge[i].next)
- {
- ll y=edge[i].t;
- if(!idx[y]) dfs2(y,y);
- }
- }
- void push_up(ll p)
- {
- tr[p].val=tr[p<<1].val+tr[p<<1|1].val;
- }
- void build(ll p,ll l,ll r)
- {
- tr[p].l=l;tr[p].r=r;
- tr[p].siz=r-l+1;
- if(l==r)
- {
- tr[p].val=0;
- tr[p].add=-1;
- return;
- }
- ll mid=l+r>>1;
- build(p<<1,l,mid);
- build(p<<1|1,mid+1,r);
- }
- void push_down(ll p)
- {
- if(tr[p].add==-1) return;
- tr[p<<1].add=tr[p<<1|1].add=tr[p].add;
- if(tr[p].add==0)
- {
- tr[p<<1].val=tr[p<<1|1].val=0;
- }
- else if(tr[p].add==1)
- {
- tr[p<<1].val=tr[p<<1].siz;
- tr[p<<1|1].val=tr[p<<1|1].siz;
- }
- tr[p].add=-1;
- }
- ll get_sum(ll p,ll l,ll r)//子树使用,途径置0
- {
- if(tr[p].l>=l&&tr[p].r<=r)
- {
- ll ans=tr[p].val;
- tr[p].val=0;
- tr[p].add=0;
- return ans;
- }
- push_down(p);
- ll ans=0;
- ll mid=tr[p].l+tr[p].r>>1;
- if(l<=mid) ans+=get_sum(p<<1,l,r);
- if(r>mid) ans+=get_sum(p<<1|1,l,r);
- push_up(p);
- return ans;
- }
- ll sum(ll p,ll l,ll r)
- {
- if(tr[p].l>=l&&tr[p].r<=r)
- {
- ll ans=tr[p].val;
- tr[p].val=tr[p].siz;
- tr[p].add=1;
- return tr[p].siz-ans;
- }
- push_down(p);
- ll ans=0;
- ll mid=tr[p].l+tr[p].r>>1;
- if(l<=mid) ans+=sum(p<<1,l,r);
- if(r>mid) ans+=sum(p<<1|1,l,r);
- push_up(p);
- return ans;
- }
- ll trson_sum(ll x)
- {
- return get_sum(1,idx[x],idx[x]+siz[x]-1);
- }
- ll tree_sum(ll x,ll y)
- {
- ll ans=0;
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - ans+=sum(1,idx[top[x]],idx[x]);
- x=fa[top[x]];
- }
- if(dep[x]>dep[y]) swap(x,y);
- ans+=sum(1,idx[x],idx[y]);
- return ans;
- }
- int main()
- {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
- memset(head,-1,sizeof head);
- cin>>n;
- for(int i=2;i<=n;++i)
- {
- cin>>fir;
- add_edge(fir+1,i,1);
- add_edge(i,fir+1,1);
- }
- rt=1;
- init_dfs(rt,0,1);
- dfs2(rt,rt);
- build(1,1,n);
- // for(int i=1;i<=n;++i) cout<
- // cout<
- cin>>m;
- while(m--)
- {
- cin>>op;
- cin>>x;
- x++;
- if(op[0]=='i')
- {
- cout<<tree_sum(x,1)<
- continue;
- }
- else cout<<trson_sum(x)<
- }
- }
大意:

思路:
大同小异
- #include
- using namespace std;
- #define ll long long
- #define endl '\n'
- const ll N=1e5+10;
- struct ty
- {
- ll t,l,next;
- }edge[N<<1];
- ll cn=0;
- ll head[N];
- void add_edge(ll a,ll b,ll c)
- {
- edge[++cn].t=b;
- edge[cn].l=c;
- edge[cn].next=head[a];
- head[a]=cn;
- }
- struct tree
- {
- ll sum=0;
- ll add=0;
- ll l,r,size=0;
- }tr[N<<2];
- ll siz[N],son[N],fa[N],dep[N],mas[N],a[N],idx[N],top[N];
- ll n,m,rt,cnt,fir,sec,x,y,z,op;
- void init_dfs(ll id,ll p,ll depth)
- {
- dep[id]=depth;
- siz[id]=1;
- fa[id]=p;
- ll son_val=0;
- for(int i=head[id];i!=-1;i=edge[i].next)
- {
- ll y=edge[i].t;
- if(y==p) continue;
- init_dfs(y,id,depth+1);
- siz[id]+=siz[y];
- if(siz[y]>son_val)
- {
- son_val=siz[y];
- son[id]=y;
- }
- }
- }
- void dfs2(ll id,ll p)
- {
- idx[id]=++cnt;
- top[id]=p;
- a[cnt]=mas[id];
- if(!son[id]) return;
- dfs2(son[id],p);
- for(int i=head[id];i!=-1;i=edge[i].next)
- {
- ll y=edge[i].t;
- if(!idx[y]) dfs2(y,y);
- }
- }
- void push_up(ll p)
- {
- tr[p].sum=tr[p<<1].sum+tr[p<<1|1].sum;
- }
- void push_down(ll p)
- {
- if(tr[p].add==0) return;
- tr[p<<1].sum+=tr[p<<1].size*tr[p].add;
- tr[p<<1|1].sum+=tr[p<<1|1].size*tr[p].add;
- tr[p<<1].add+=tr[p].add;
- tr[p<<1|1].add+=tr[p].add;
- tr[p].add=0;
- }
- void build(ll p,ll l,ll r)
- {
- tr[p].l=l;tr[p].r=r;tr[p].size=r-l+1;
- if(l==r)
- {
- tr[p].sum=a[l];
- return;
- }
- ll mid=l+r>>1;
- build(p<<1,l,mid);
- build(p<<1|1,mid+1,r);
- push_up(p);
- }
- void change(ll p,ll l,ll val)
- {
- if(tr[p].l==tr[p].r)
- {
- tr[p].sum+=val;
- return;
- }
- push_down(p);
- ll mid=tr[p].l+tr[p].r>>1;
- if(l<=mid) change(p<<1,l,val);
- else change(p<<1|1,l,val);
- push_up(p);
- }
- void add(ll p,ll l,ll r,ll val)
- {
- if(tr[p].l>=l&&tr[p].r<=r)
- {
- tr[p].sum+=tr[p].size*val;
- tr[p].add+=val;
- return;
- }
- push_down(p);
- ll mid=tr[p].l+tr[p].r>>1;
- if(l<=mid) add(p<<1,l,r,val);
- if(r>mid) add(p<<1|1,l,r,val);
- push_up(p);
- }
- ll sum(ll p,ll l,ll r)
- {
- if(tr[p].l>=l&&tr[p].r<=r)
- {
- return tr[p].sum;
- }
- ll ans=0;
- push_down(p);
- ll mid=tr[p].l+tr[p].r>>1;
- if(l<=mid) ans+=sum(p<<1,l,r);
- if(r>mid) ans+=sum(p<<1|1,l,r);
- return ans;
- }
- ll tree_sum(ll x,ll y)
- {
- ll ans=0;
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - ans+=sum(1,idx[top[x]],idx[x]);
- x=fa[top[x]];
- }
- if(dep[x]>dep[y]) swap(x,y);
- ans+=sum(1,idx[x],idx[y]);
- return ans;
- }
- void trson_add(ll x,ll val)
- {
- add(1,idx[x],idx[x]+siz[x]-1,val);
- }
- int main()
- {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
- memset(head,-1,sizeof head);
- cin>>n>>m;
- for(int i=1;i<=n;++i) cin>>mas[i];
- for(int i=1;i
- {
- cin>>fir>>sec;
- add_edge(fir,sec,1);
- add_edge(sec,fir,1);
- }
- rt=1;
- init_dfs(rt,0,1);
- dfs2(rt,rt);
- build(1,1,n);
- while(m--)
- {
- cin>>op;
- if(op==1)
- {
- cin>>x>>y;
- change(1,idx[x],y);
- continue;
- }
- if(op==2)
- {
- cin>>x>>y;
- trson_add(x,y);
- continue;
- }
- if(op==3)
- {
- cin>>x;
- cout<<tree_sum(x,1)<
- }
-
- }
- return 0;
- }
进阶
大意:
给定一棵树,以及一些多余的边。
对于每一条树上的边,可以取一条多余的边来替代它,使得该边两端的点仍然联通。如果该操作可行,求出改变后的两点最短距离 .不行的话,输出-1
思路:
正向思考可能比较难,因为对于每一条边都要考虑所有多余的边能否替换它。既然如此,不妨换个思路,考虑每一条边能更新什么。

不难发现,对于一条多余的边,它的两边的点的路径与它可以构成一个环(见图)。环上的点都是可以相互到达的,所以这条多余的边就可以更新环上的所有边。如果我们在每一条边维护一下可以更新它的边的最小边权,这样的话,不就是路径更新最小值嘛。为了使用树剖,我们再记录一下每一条边对应的较深的节点,那么边就与节点建立了一对一的映射,就可以快乐树剖了。
当然也可以不用树剖。我们发现这个过程还是有很多重复的操作,可以考虑使用并查集。
对于所有多余的边,按边权从小到大排序,那么每一条边就尽可能更新它能更新的边,如果某一条边发现已经被更新过了,就跳过,因为它肯定是被更小的边更新的,由该边更新,结果不会更优。这个跳过的操作,用并查集维护就可以了。
求lca时也可以用一下树剖
- #include
- using namespace std;
- #define ll long long
- #define endl '\n'
- const ll N=1e5+10;
- struct ty
- {
- ll t,l,next;
- }edge[N<<1];
- ll cn=0;
- ll head[N];
- void add(ll a,ll b,ll c)
- {
- edge[++cn].t=b;
- edge[cn].l=c;
- edge[cn].next=head[a];
- head[a]=cn;
- }
- struct road
- {
- ll x,y;
- ll val;
- }q[N];
- bool cmp(road a,road b)
- {
- return a.val
- }
- ll siz[N],fa[N],dep[N],top[N],idx[N],a[N],mas[N],son[N];
- ll cnt,fir,sec,n,m,rt,x,y,z;
- map
,ll> mp; - map
ms; - void init_dfs(ll id,ll p,ll depth)
- {
- fa[id]=p;
- siz[id]=1;
- ms[id]=mp[make_pair(id,p)];//让儿子与边建立映射
- dep[id]=depth+1;
- ll son_val=0;
- for(int i=head[id];i!=-1;i=edge[i].next)
- {
- ll y=edge[i].t;
- if(y==p) continue;
- init_dfs(y,id,depth+1);
- siz[id]+=siz[y];
- if(siz[y]>son_val)
- {
- son_val=siz[y];
- son[id]=y;
- }
- }
- }
- void dfs2(ll id,ll p)
- {
- top[id]=p;
- idx[id]=++cnt;
- a[cnt]=mas[id];
- if(!son[id]) return;
- dfs2(son[id],p);
- for(int i=head[id];i!=-1;i=edge[i].next)
- {
- ll y=edge[i].t;
- if(!idx[y]) dfs2(y,y);
- }
- }
- ll get_lca(ll x,ll y)
- {
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - x=fa[top[x]];
- }
- return dep[x]>dep[y]?y:x;
- }
- ll f[N];
- ll find(ll x)
- {
- return f[x]==x?f[x]:f[x]=find(f[x]);
- }
- ll ans[N];
- int main()
- {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
- memset(head,-1,sizeof head);
- memset(ans,-1,sizeof ans);
- cin>>n>>m;
- for(int i=1;i
- {
- cin>>fir>>sec;
- add(fir,sec,1);
- add(sec,fir,1);
- mp[make_pair(fir,sec)]=i;//边与两个点建立映射
- mp[make_pair(sec,fir)]=i;
- }
- rt=1;
- init_dfs(rt,0,1);
- dfs2(rt,rt);
- for(int i=1;i<=m;++i)
- {
- cin>>q[i].x>>q[i].y>>q[i].val;
- }
- sort(q+1,q+1+m,cmp);
- for(int i=1;i<=n;++i) f[i]=i;
- for(int i=1;i<=m;++i)
- {
- ll fx=find(q[i].x);ll fy=find(q[i].y);
- ll LCA=get_lca(fx,fy);
- while(dep[fx]>dep[LCA])
- {
- //cout<
- ans[ms[fx]]=q[i].val;
- f[fx]=find(fa[fx]);//注意要跳到父亲节点的对应的第一个可以更新的点
- fx=f[fa[fx]];
- }
- while(dep[fy]>dep[LCA])
- {
- //cout<
- ans[ms[fy]]=q[i].val;
- f[fy]=find(fa[fy]);//注意要跳到父亲节点的对应的第一个可以更新的点
- fy=f[fa[fy]];
- }
-
- }
- for(int i=1;i
- return 0;
- }
大意:

区间覆盖,求区间颜色段数
思路:
维护一下线段树的左端点和右端点的颜色,每次合并的时候,让两个子区间的val值相加,如果左儿子的右端点颜色=右儿子左端点颜色,就让val-1即可。
那么查询的时候,就把每一条经过的重链区间的val相加,同样的,如果某一条链的top的颜色=其父节点的颜色,val-1
- #include
- using namespace std;
- #define ll long long
- #define endl '\n'
- const ll N=1e5+10;
- struct ty
- {
- ll t,l,next;
- }edge[N<<1];
- ll cn=0;
- ll head[N];
- void add_edge(ll a,ll b,ll c)
- {
- edge[++cn].t=b;
- edge[cn].l=c;
- edge[cn].next=head[a];
- head[a]=cn;
- }
- struct tree
- {
- ll l,r;
- ll lm,rm;
- ll siz;
- ll add;
- ll val;
- }tr[N<<2];
- ll siz[N],fa[N],top[N],son[N],a[N],idx[N],mas[N],dep[N];
- ll n,m,rt,cnt,fir,sec,x,y,z;
- char op;
- void init_dfs(ll id,ll p,ll depth)
- {
- dep[id]=depth;
- fa[id]=p;
- siz[id]=1;
- ll son_val=0;
- for(int i=head[id];i!=-1;i=edge[i].next)
- {
- ll y=edge[i].t;
- if(y==p) continue;
- init_dfs(y,id,depth+1);
- siz[id]+=siz[y];
- if(siz[y]>son_val)
- {
- son_val=siz[y];
- son[id]=y;
- }
- }
- }
- void dfs2(ll id,ll p)
- {
- top[id]=p;
- idx[id]=++cnt;
- a[cnt]=mas[id];
- if(!son[id]) return;
- dfs2(son[id],p);
- for(int i=head[id];i!=-1;i=edge[i].next)
- {
- ll y=edge[i].t;
- if(!idx[y]) dfs2(y,y);
- }
- }
- void push_up(ll p)
- {
- tr[p].val=tr[p<<1].val+tr[p<<1|1].val;
- if(tr[p<<1].rm==tr[p<<1|1].lm) tr[p].val-=1;
- tr[p].lm=tr[p<<1].lm;
- tr[p].rm=tr[p<<1|1].rm;
- }
- void push_down(ll p)
- {
- if(tr[p].add==0) return;
- tr[p<<1].add=tr[p<<1|1].add=tr[p].add;
- tr[p<<1].lm=tr[p<<1].rm=tr[p].add;
- tr[p<<1|1].lm=tr[p<<1|1].rm=tr[p].add;
- tr[p<<1].val=tr[p<<1|1].val=1;
- tr[p].add=0;
- }
- void build(ll p,ll l,ll r)
- {
- tr[p].l=l;tr[p].r=r;tr[p].siz=r-l+1;
- if(l==r)
- {
- tr[p].val=1;
- tr[p].lm=tr[p].rm=a[l];
- return;
- }
- ll mid=l+r>>1;
- build(p<<1,l,mid);
- build(p<<1|1,mid+1,r);
- push_up(p);
- }
- void change(ll p,ll l,ll r,ll val)
- {
- if(tr[p].l>=l&&tr[p].r<=r)
- {
- tr[p].val=1;
- tr[p].lm=tr[p].rm=val;
- tr[p].add=val;
- return;
- }
- push_down(p);
- ll mid=tr[p].l+tr[p].r>>1;
- if(r<=mid)
- {
- change(p<<1,l,r,val);
- }
- else if(l>mid)
- {
- change(p<<1|1,l,r,val);
- }
- else
- {
- change(p<<1,l,r,val);
- change(p<<1|1,l,r,val);
- }
- // if(l<=mid) change(p<<1,l,r,val);
- // if(r>mid) change(p<<1|1,l,r,val);
- push_up(p);
- }
- ll sum(ll p,ll l,ll r)
- {
- if(tr[p].l>=l&&tr[p].r<=r)
- {
- return tr[p].val;
- }
- push_down(p);
- ll mid=tr[p].l+tr[p].r>>1;
- ll ans=0;
- if(r<=mid) return sum(p<<1,l,r);
- if(l>mid) return sum(p<<1|1,l,r);
- ans+=sum(p<<1,l,r);ans+=sum(p<<1|1,l,r);
- if(tr[p<<1].rm==tr[p<<1|1].lm) ans--;
- return ans;
- }
- ll col(ll p,ll l)
- {
- if(tr[p].l==tr[p].r&&tr[p].l==l)
- {
- return tr[p].lm;
- }
- push_down(p);
- ll mid=tr[p].l+tr[p].r>>1;
- if(l<=mid) return col(p<<1,l);
- else return col(p<<1|1,l);
- }
- void tree_change(ll x,ll y,ll val)
- {
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - change(1,idx[top[x]],idx[x],val);
- x=fa[top[x]];
- }
- if(dep[x]>dep[y]) swap(x,y);
- change(1,idx[x],idx[y],val);
- }
- ll tree_sum(ll x,ll y)
- {
- ll ans=0;
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - ans+=sum(1,idx[top[x]],idx[x]);
- if(col(1,idx[top[x]])==col(1,idx[fa[top[x]]])) ans--;
- x=fa[top[x]];
- }
- if(dep[x]>dep[y]) swap(x,y);
- ans+=sum(1,idx[x],idx[y]);
- return ans;
- }
- int main()
- {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
- //注意push_up时对左右边界的更新
- memset(head,-1,sizeof head);
- cin>>n>>m;
- for(int i=1;i<=n;++i) cin>>mas[i];
- for(int i=1;i
- {
- cin>>fir>>sec;
- add_edge(fir,sec,1);
- add_edge(sec,fir,1);
- }
- rt=1;
- init_dfs(rt,0,1);
- dfs2(rt,rt);
- build(1,1,n);
- // cout<
- while(m--)
- {
- cin>>op;
- if(op=='C')
- {
- cin>>x>>y>>z;
- tree_change(x,y,z);
- continue;
- }
- cin>>x>>y;
- cout<<tree_sum(x,y)<
- }
- }
大意:
换根,区间覆盖,查询子树最小值
思路:
讲一下换根。其实跟第一题的区别不算很大
令root为当前根,x为要求的根,lca为两者的lca
1.u==root:直接输出整棵树的最小值就可以了
2.lca!=x:x的子树没有变,就正常查询就可以了
3.lca==x:x的子树就变成了x到root路径上第一个儿子的子树的补集,考虑到子树的dfn序是连续的,所以我们可以把查询区间分成两个部分,一个是dfn序小于第一个儿子子树dfn序的区间,一个是dfn序大于第一个儿子子树dfn序的区间。然后合并求最小值就可以了啦
- #include
- using namespace std;
- #define ll long long
- #define endl '\n'
- const ll N=1e5+10;
- struct ty
- {
- ll t,l,next;
- }edge[N<<1];
- ll cn=0;
- ll head[N];
- void add_edge(ll a,ll b,ll c)
- {
- edge[++cn].t=b;
- edge[cn].l=c;
- edge[cn].next=head[a];
- head[a]=cn;
- }
- struct tree
- {
- ll l,r;
- ll siz;
- ll add=-1;
- ll val;
- }tr[N<<2];
- ll siz[N],fa[N],son[N],dep[N],idx[N],a[N],mas[N],top[N];
- ll n,m,rt,cnt,fir,sec,x,y,z,op;
- void init_dfs(ll id,ll p,ll depth)
- {
- dep[id]=depth;
- fa[id]=p;
- siz[id]=1;
- ll son_val=0;
- for(int i=head[id];i!=-1;i=edge[i].next)
- {
- ll y=edge[i].t;
- if(y==p) continue;
- init_dfs(y,id,depth+1);
- siz[id]+=siz[y];
- if(siz[y]>son_val)
- {
- son_val=siz[y];
- son[id]=y;
- }
- }
- }
- void dfs2(ll id,ll p)
- {
- top[id]=p;
- idx[id]=++cnt;
- a[cnt]=mas[id];
- if(!son[id]) return;
- dfs2(son[id],p);
- for(int i=head[id];i!=-1;i=edge[i].next)
- {
- ll y=edge[i].t;
- if(!idx[y]) dfs2(y,y);
- }
- }
- void push_up(ll p)
- {
- tr[p].val=min(tr[p<<1].val,tr[p<<1|1].val);
- }
- void push_down(ll p)
- {
- if(tr[p].add==-1) return;
- tr[p<<1].add=tr[p<<1|1].add=tr[p].add;
- tr[p<<1].val=tr[p<<1|1].val=tr[p].add;
- tr[p].add=-1;
- }
- void build(ll p,ll l,ll r)
- {
- tr[p].l=l;tr[p].r=r;tr[p].siz=r-l+1;
- if(l==r)
- {
- tr[p].val=a[l];
- return;
- }
- ll mid=r+l>>1;
- build(p<<1,l,mid);
- build(p<<1|1,mid+1,r);
- push_up(p);
- }
- void change(ll p,ll l,ll r,ll val)
- {
- if(tr[p].l>=l&&tr[p].r<=r)
- {
- tr[p].add=tr[p].val=val;
- return;
- }
- push_down(p);
- ll mid=tr[p].l+tr[p].r>>1;
- if(l<=mid) change(p<<1,l,r,val);
- if(r>mid) change(p<<1|1,l,r,val);
- push_up(p);
- }
- void tree_change(ll x,ll y,ll val)
- {
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - change(1,idx[top[x]],idx[x],val);
- x=fa[top[x]];
- }
- if(dep[x]>dep[y]) swap(x,y);
- change(1,idx[x],idx[y],val);
- }
- ll sum(ll p,ll l,ll r)
- {
- if(tr[p].l>=l&&tr[p].r<=r)
- {
- return tr[p].val;
- }
- ll ans=1e16;
- push_down(p);
- ll mid=tr[p].l+tr[p].r>>1;
- if(l<=mid) ans=min(ans,sum(p<<1,l,r));
- if(r>mid) ans=min(ans,sum(p<<1|1,l,r));
- return ans;
- }
- ll get_lca(ll x,ll y)
- {
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - x=fa[top[x]];
- }
- return dep[x]>dep[y]?y:x;
- }
- ll find_firson(ll x,ll y)//找打x到y路径上的第一个儿子
- {
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - if(fa[top[x]]==y) return top[x];
- x=fa[top[x]];
- }
- if(dep[x]>dep[y]) swap(x,y);
- return son[x];
- }
- ll trson_sum(ll x)
- {
- if(x==rt) return sum(1,1,n);
- ll LCA=get_lca(x,rt);
- if(LCA!=x) return sum(1,idx[x],idx[x]+siz[x]-1);
- ll fir_son=find_firson(x,rt);
- ll ans1=sum(1,1,idx[fir_son]-1);
- ll ans2=sum(1,idx[rt]+siz[rt],n);
- return min(ans1,ans2);
- }
- int main()
- {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
- memset(head,-1,sizeof head);
- cin>>n>>m;
- for(int i=1;i
- {
- cin>>fir>>sec;
- add_edge(fir,sec,1);
- add_edge(sec,fir,1);
- }
- for(int i=1;i<=n;++i) cin>>mas[i];
- cin>>rt;
- init_dfs(1,0,1);
- dfs2(1,1);
- build(1,1,n);
- while(m--)
- {
- cin>>op;
- if(op==1)
- {
- cin>>x;
- rt=x;
- continue;
- }
- else if(op==2)
- {
- cin>>x>>y>>z;
- tree_change(x,y,z);
- continue;
- }
- else
- {
- cin>>x;
- cout<<trson_sum(x)<
- }
- }
- }
大意:
对一段区间,查询是否存在特定值
思路:
直接维护所有颜色是不现实的,所以不妨将答案离线下来,按大小升序排序。那么,每次遍历到一种颜色的时候,我们再在线段树上更新对应的颜色,如此,我们只要维护区间最大值就可以了,如果最大值=颜色,就是可以的,否则一定不行,那么就没了(还是钻了没有修改操作的空子嘿嘿)
- #include
- using namespace std;
- #define ll long long
- #define endl '\n'
- const ll N=1e5+10;
- struct ty
- {
- ll t,l,next;
- }edge[N<<1];
- ll cn=0;
- ll head[N];
- void add_edge(ll a,ll b,ll c)
- {
- edge[++cn].t=b;
- edge[cn].l=c;
- edge[cn].next=head[a];
- head[a]=cn;
- }
- struct tree
- {
- ll l,r;
- ll val;
- ll add;
- ll siz;
- }tr[N<<2];
- vector
color[N]; - ll siz[N],dep[N],top[N],fa[N],son[N],a[N],mas[N],idx[N];
- ll n,m,fir,sec,cnt,x,y,z,rt;
- ll ans[N];
- bool vis[N];
- struct query
- {
- ll x,y,z;
- ll id;
- }q[N];
- bool cmp(query a,query b)
- {
- return a.z
- }
- void init_dfs(ll id,ll p,ll depth)
- {
- dep[id]=depth;
- fa[id]=p;
- siz[id]=1;
- ll son_val=0;
- for(int i=head[id];i!=-1;i=edge[i].next)
- {
- ll y=edge[i].t;
- if(y==p) continue;
- init_dfs(y,id,depth+1);
- siz[id]+=siz[y];
- if(siz[y]>son_val)
- {
- son_val=siz[y];
- son[id]=y;
- }
- }
- }
- void dfs2(ll id,ll p)
- {
- top[id]=p;
- idx[id]=++cnt;
- a[cnt]=mas[id];
- if(!son[id]) return;
- dfs2(son[id],p);
- for(int i=head[id];i!=-1;i=edge[i].next)
- {
- ll y=edge[i].t;
- if(!idx[y]) dfs2(y,y);
- }
- }
- void push_up(ll p)
- {
- tr[p].val=max(tr[p<<1].val,tr[p<<1|1].val);
- }
- void build(ll p,ll l,ll r)
- {
- tr[p].l=l;tr[p].r=r;
- tr[p].siz=r-l+1;
- if(l==r){
- tr[p].val=a[l];
- return;
- }
- ll mid=tr[p].l+tr[p].r>>1;
- build(p<<1,l,mid);
- build(p<<1|1,mid+1,r);
- push_up(p);
- }
- void change(ll p,ll l,ll r,ll val)
- {
- if(tr[p].l>=l&&tr[p].r<=r)
- {
- tr[p].val=val;
- return;
- }
- ll mid=tr[p].l+tr[p].r>>1;
- if(l<=mid) change(p<<1,l,r,val);
- if(r>mid) change(p<<1|1,l,r,val);
- push_up(p);
- }
- ll sum(ll p,ll l,ll r,ll val)
- {
- if(tr[p].l>=l&&tr[p].r<=r)
- {
- return tr[p].val==val;
- }
- ll mid=tr[p].l+tr[p].r>>1;
- if(l<=mid) if(sum(p<<1,l,r,val)) return 1;
- if(r>mid) if(sum(p<<1|1,l,r,val)) return 1;
- return 0;
- }
- ll tree_sum(ll x,ll y,ll val)
- {
- while(top[x]!=top[y])
- {
- if(dep[top[x]]
swap(x,y); - if(sum(1,idx[top[x]],idx[x],val)) return 1;
- x=fa[top[x]];
- }
- if(dep[x]>dep[y]) swap(x,y);
- if(sum(1,idx[x],idx[y],val)) return 1;
- return 0;
- }
- int main()
- {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
- memset(head,-1,sizeof head);
- cin>>n>>m;
- for(int i=1;i<=n;++i)
- {
- cin>>x;
- color[x].push_back(i);
- }
- for(int i=1;i
- {
- cin>>fir>>sec;
- add_edge(fir,sec,1);
- add_edge(sec,fir,1);
- }
- for(int i=1;i<=m;++i)
- {
- cin>>q[i].x>>q[i].y>>q[i].z;
- q[i].id=i;
- }
- rt=1;
- init_dfs(rt,0,1);
- dfs2(rt,rt);
- build(1,1,n);
- sort(q+1,q+1+m,cmp);
- for(int i=1;i<=m;++i)
- {
- ll col=q[i].z;
- if(color[col].size()==0) continue;
- if(!vis[col])
- {
- for(int k:color[col])
- {
- change(1,idx[k],idx[k],col);
- }
- }
- vis[col]=1;
- ans[q[i].id]=tree_sum(q[i].x,q[i].y,q[i].z);
- }
- for(int i=1;i<=m;++i) cout<
- }
更新中...
-
相关阅读:
火热报名中 | 2天峰会、20+热门议题,AutoESG 2023数智低碳---中国汽车碳管理创新峰会亮点抢先看!
Excel 导出打不开
使用ES检索PDF或Word等格式文件方案
Pytest系列(31) - config.cache 使用
00Hadoop数据仓库平台
论文笔记 - SIMILAR: Submodular Information Measures Based Active Learning In Realistic Scenarios
【MySQL】MySQL数据库锁使用与InnoDB加锁的原理解析(MySQL专栏启动)
关于队头阻塞的一些笔记
ONLYOFFICE 文档 8.1 现已发布:功能全面的 PDF 编辑器、幻灯片版式等等
STM32 定时器问题
-
原文地址:https://blog.csdn.net/sophilex/article/details/126746589