【bzoj3589】动态树【树链剖分】【线段树】
题目链接
题解
首先,第一个操作就是子树加,直接线段树对应dfn序区间加即可。
第二个操作,我写了个奇怪的东西,单次查询大概是两个log的,常数大上天……其实,这个过程类似于线段覆盖。我们可以维护
setv:该区间是否被完全覆盖
tot:该区间被覆盖的个数。
然后我们在线段树上,对着没覆盖完的节点走一遍。先找到查询的区间,然后遇到完全覆盖的节点和叶子节点返回,完全未覆盖的区间更新答案和标记再返回,否则往左右儿子走。最后把变了的节点复原。细节见代码。
然后就A了。
代码
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=200005;
int n,m,op,k,u,v,cnt,ans,head[N],to[N*2],nxt[N*2];
int idx,fa[N],dep[N],siz[N],son[N],dfn[N],pos[N],top[N];
int sumv[N*4],setv[N*4],tot[N*4],tag1[N*4],tag2[N*4],fix[N*8];
void adde(int u,int v){
to[++cnt]=v;
nxt[cnt]=head[u];
head[u]=cnt;
}
void dfs(int u){
siz[u]=1;
int v;
for(int i=head[u];i;i=nxt[i]){
v=to[i];
if(v!=fa[u]){
fa[v]=u;
dep[v]=dep[u]+1;
dfs(v);
siz[u]+=siz[v];
if(!son[u]||siz[son[u]]<siz[v]){
son[u]=v;
}
}
}
}
void dfs(int u,int tp){
dfn[u]=++idx;
pos[idx]=u;
top[u]=tp;
if(son[u]){
dfs(son[u],tp);
}
int v;
for(int i=head[u];i;i=nxt[i]){
v=to[i];
if(v!=fa[u]&&v!=son[u]){
dfs(v,v);
}
}
}
void pushdown(int o,int l,int r){
int mid=(l+r)/2;
if(tag1[o]){
tag1[o*2]+=tag1[o];
tag1[o*2+1]+=tag1[o];
sumv[o*2]+=tag1[o]*(mid-l+1);
sumv[o*2+1]+=tag1[o]*(r-mid);
tag1[o]=0;
}
if(tag2[o]){
tag2[o*2]=tag2[o*2+1]=setv[o*2]=setv[o*2+1]=1;
tot[o*2]=mid-l+1;
tot[o*2+1]=r-mid;
fix[++fix[0]]=o*2;
fix[++fix[0]]=o*2+1;
tag2[o]=0;
}
}
void update(int o,int l,int r,int L,int R,int v){
if(L<=l&&R>=r){
sumv[o]+=v*(r-l+1);
tag1[o]+=v;
return;
}
pushdown(o,l,r);
int mid=(l+r)/2;
if(L<=mid){
update(o*2,l,mid,L,R,v);
}
if(R>mid){
update(o*2+1,mid+1,r,L,R,v);
}
sumv[o]=sumv[o*2]+sumv[o*2+1];
}
void query(int o,int l,int r,int L,int R){
fix[++fix[0]]=o;
if(L<=l&&R>=r){
if(!tot[o]){
ans+=sumv[o];
setv[o]=tag2[o]=1;
tot[o]=r-l+1;
fix[++fix[0]]=o;
return;
}
if(l==r||setv[o]){
return;
}
pushdown(o,l,r);
int mid=(l+r)/2;
if(tot[o*2]<mid-l+1){
query(o*2,l,mid,L,R);
}
if(tot[o*2+1]<r-mid){
query(o*2+1,mid+1,r,L,R);
}
setv[o]=setv[o*2]&&setv[o*2+1];
tot[o]=tot[o*2]+tot[o*2+1];
return;
}
pushdown(o,l,r);
int mid=(l+r)/2;
if(L<=mid){
query(o*2,l,mid,L,R);
}
if(R>mid){
query(o*2+1,mid+1,r,L,R);
}
setv[o]=setv[o*2]&&setv[o*2+1];
tot[o]=tot[o*2]+tot[o*2+1];
}
int main(){
scanf("%d",&n);
for(int i=1;i<n;i++){
scanf("%d%d",&u,&v);
adde(u,v);
adde(v,u);
}
dfs(1);
dfs(1,1);
scanf("%d",&m);
while(m--){
scanf("%d",&op);
if(!op){
scanf("%d%d",&u,&v);
update(1,1,n,dfn[u],dfn[u]+siz[u]-1,v);
}else{
scanf("%d",&k);
ans=0;
while(k--){
scanf("%d%d",&u,&v);
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]){
swap(u,v);
}
query(1,1,n,dfn[top[u]],dfn[u]);
u=fa[top[u]];
}
if(dep[u]>dep[v]){
swap(u,v);
}
query(1,1,n,dfn[u],dfn[v]);
}
printf("%d\n",ans&2147483647);
for(int i=1;i<=fix[0];i++){
setv[fix[i]]=tag2[fix[i]]=tot[fix[i]]=0;
}
fix[0]=0;
}
}
return 0;
}