[BZOJ3589] 动态树
Description
别忘了这是一棵动态树, 每时每刻都是动态的. 小明要求你在这棵树上维护两种事件事件0:这棵树长出了一些果子, 即某个子树中的每个节点都会长出K个果子.
事件1:小明希望你求出几条树枝上的果子数. 一条树枝其实就是一个从某个节点到根的路径的一段. 每次小明会选定一些树枝, 让你求出在这些树枝上的节点的果子数的和. 注意, 树枝之间可能会重合, 这时重合的部分的节点的果子只要算一次.
Input
第一行一个整数n(1<=n<=200,000), 即节点数.
接下来n-1行, 每行两个数字u, v. 表示果子u和果子v之间有一条直接的边. 节点从1开始编号.
在接下来一个整数nQ(1<=nQ<=200,000), 表示事件.
最后nQ行, 每行开头要么是0, 要么是1.
如果是0, 表示这个事件是事件0. 这行接下来的2个整数u, delta表示以u为根的子树中的每个节点长出了delta个果子.
如果是1, 表示这个事件是事件1. 这行接下来一个整数K(1<=K<=5), 表示这次询问涉及K个树枝. 接下来K对整数u_k, v_k, 每个树枝从节点u_k到节点v_k. 由于果子数可能非常多, 请输出这个数模2^31的结果.
Output
对于每个事件1, 输出询问的果子数.
Sample Input
5
1 2
2 3
2 4
1 5
3
0 1 1
0 2 3
1 2 3 1 1 4
Sample Output
13
试题分析
懒得写容斥?线段树上标记大法好。
直接对于每个询问在线段树上再维护一个数组,然后照样update,最后再来一遍清空即可。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
#define LL long long
inline int read(){
int x=0,f=1; char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*f;
}
#define INF 0x7fffffff
const int MAXN = 400010;
int dep[MAXN+1],sz[MAXN+1],Hvs[MAXN+1],top[MAXN+1];
int fa[MAXN+1]; int Root[MAXN<<1],Next[MAXN<<1],Node[MAXN<<1],cnt;
int tr[MAXN<<2]; int vis[MAXN<<2]; int Don[MAXN+1];
int dfn[MAXN+1]; int N,M; int tim; int R[MAXN+1];
int col[MAXN<<2];
inline void insert(int u,int v){
Node[++cnt]=v; Next[cnt]=Root[u]; Root[u]=cnt; return ;
}
inline void dfs1(int k,int Fa){
fa[k]=Fa; sz[k]=1; dep[k]=dep[Fa]+1;
for(int x=Root[k];x;x=Next[x]){
int v=Node[x]; if(v==Fa) continue;
dfs1(v,k); sz[k]+=sz[v];
if(!Hvs[k]||sz[Hvs[k]]<sz[v]) Hvs[k]=v;
} return ;
}
inline void dfs2(int k,int Tp){
top[k]=Tp; dfn[k]=++tim; Don[tim]=k;
if(Hvs[k]) dfs2(Hvs[k],Tp);
for(int x=Root[k];x;x=Next[x]){
int v=Node[x]; if(v==fa[k]||v==Hvs[k]) continue;
dfs2(v,v);
} R[k]=tim; return ;
}
inline void tage_lazy(int rt,int l,int r){
if(col[rt]){
int mid=(l+r)>>1;
col[rt<<1]+=col[rt]; col[rt<<1|1]+=col[rt];
tr[rt<<1]+=(mid-l+1)*col[rt]; tr[rt<<1|1]+=(r-mid)*col[rt];
col[rt]=0;
}
}
inline void Add(int rt,int l,int r,int L,int R,int x){
if(L<=l&&R>=r) {tr[rt]+=x*(r-l+1); col[rt]+=x; return ;} tage_lazy(rt,l,r);
int mid=(l+r)>>1; if(L<=mid) Add(rt<<1,l,mid,L,R,x);
if(R>mid) Add(rt<<1|1,mid+1,r,L,R,x);
tr[rt]=tr[rt<<1]+tr[rt<<1|1]; return ;
}
int u[31],v[31];
int ans;
inline void Query(int rt,int l,int r,int L,int R){
//cout<<vis[rt]<<":"<<l<<" "<<r<<" "<<tr[rt]<<" "<<L<<" "<<R<<endl;
if(L<=l&&R>=r){vis[rt]=tr[rt]; return ;} tage_lazy(rt,l,r);
int mid=(l+r)>>1; if(L<=mid) Query(rt<<1,l,mid,L,R);
if(R>mid) Query(rt<<1|1,mid+1,r,L,R); //if(!vis[rt]) vis[rt]=((vis[rt<<1]|vis[rt<<1|1])?2:0);
if(vis[rt]!=tr[rt]) vis[rt]=vis[rt<<1]+vis[rt<<1|1];
return ;
}
inline void Clear(int rt,int l,int r,int L,int R){
vis[rt]=0; if(L<=l&&R>=r) return ; int mid=(l+r)>>1; tage_lazy(rt,l,r);
if(L<=mid) Clear(rt<<1,l,mid,L,R);
if(R>mid) Clear(rt<<1|1,mid+1,r,L,R); return ;
}
inline void set_vis(int x,int y,int opr){
int fx=top[x],fy=top[y];
while(fx!=fy){
if(dep[x]>dep[y]) {
if(opr==1) Query(1,1,N,dfn[fx],dfn[x]);
else Clear(1,1,N,dfn[fx],dfn[x]);
x=fa[fx];
}
else {
if(opr==1) Query(1,1,N,dfn[fy],dfn[y]);
else Clear(1,1,N,dfn[fy],dfn[y]);
y=fa[fy];
} fx=top[x],fy=top[y];
} if(dfn[x]>dfn[y]) swap(x,y);
if(opr==1) Query(1,1,N,dfn[x],dfn[y]);
else Clear(1,1,N,dfn[x],dfn[y]); return ;
}
int main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
N=read();
for(int i=1;i<N;i++){
int u=read(),v=read();
insert(u,v); insert(v,u);
} dfs1(1,0); dfs2(1,1);
int Q=read();
while(Q--){
int opr=read();
if(!opr){
int k=read(),x=read();
Add(1,1,N,dfn[k],R[k],x);
//cout<<dfn[k]<<endl;
}
else{
int K=read(); //LL ans=0;
for(int i=1;i<=K;i++){
u[i]=read(),v[i]=read();
set_vis(u[i],v[i],1);
}printf("%d
",vis[1]&INF);
for(int i=1;i<=K;i++) set_vis(u[i],v[i],0);
}
}
return 0;
}