BZOJ3589: 动态树
题解:树剖sb题啊 想玩新花样啊 写虚树啊 写着写着又回到树剖了 咬牙重构树剖 写一万年发现树剖写挂了
直接考虑 对于每次查询 等于选定某些区间统计价值 直接对于需要的区间打上标记 最后统计即可 查询结束后清除标记
#include <bits/stdc++.h>
const int MAXN=3e5+10;
#define link(x) for(edge *j=h[x];j;j=j->next)
using namespace std;
struct edge{int t;edge*next;}e[MAXN<<1],*h[MAXN],*o=e;
void add(int x,int y){o->t=y;o->next=h[x];h[x]=o++;}
int dep[MAXN],son[MAXN],num[MAXN],fa[MAXN],n,m;
int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return f*x;
}
void dfs(int v,int pre,int deep){
dep[v]=deep+1;fa[v]=pre;num[v]=1;
link(v){
if(j->t!=pre){
dfs(j->t,v,deep+1);
num[v]+=num[j->t];
if(son[v]==-1||num[j->t]>num[son[v]])son[v]=j->t;
}
}
}
int p[MAXN],fp[MAXN],cnt,tp[MAXN];
void dfs1(int v,int td){
p[v]=++cnt;fp[p[v]]=v;tp[v]=td;
if(son[v]!=-1)dfs1(son[v],td);
link(v)if(son[v]!=j->t&&fa[v]!=j->t)dfs1(j->t,j->t);
}
int sum[MAXN<<2],flag[MAXN<<2],sum1[MAXN<<2];int flag1[MAXN<<2];
inline void push(int rt,int l,int r){
int mid=(l+r)>>1;
sum[rt<<1]+=1LL*(mid-l+1)*flag[rt];
sum[rt<<1|1]+=1LL*(r-mid)*flag[rt];
flag[rt<<1]+=flag[rt];flag[rt<<1|1]+=flag[rt];
flag[rt]=0;
if(flag1[rt]==1){sum1[rt<<1]=sum[rt<<1];sum1[rt<<1|1]=sum[rt<<1|1];flag1[rt<<1]=flag1[rt<<1|1]=flag1[rt];}
if(flag1[rt]==-1){sum1[rt<<1]=sum1[rt<<1|1]=0;flag1[rt<<1]=flag1[rt<<1|1]=-1;}
flag1[rt]=0;
}
inline void up(int rt){sum[rt]=sum[rt<<1]+sum[rt<<1|1];sum1[rt]=sum1[rt<<1]+sum1[rt<<1|1];}
inline void update(int rt,int l,int r,int ql,int qr,int vul){
if(ql<=l&&r<=qr){sum[rt]+=1LL*(r-l+1)*vul;flag[rt]+=vul;return ;}
int mid=(l+r)>>1;
push(rt,l,r);
if(ql<=mid)update(rt<<1,l,mid,ql,qr,vul);
if(qr>mid)update(rt<<1|1,mid+1,r,ql,qr,vul);
up(rt);
}
void update1(int rt,int l,int r,int ql,int qr,int vul){
if(ql<=l&&r<=qr){
if(vul==1)sum1[rt]=sum[rt],flag1[rt]=1;
else sum1[rt]=0,flag1[rt]=-1;
return ;
}
int mid=(l+r)>>1;
push(rt,l,r);
if(ql<=mid)update1(rt<<1,l,mid,ql,qr,vul);
if(qr>mid)update1(rt<<1|1,mid+1,r,ql,qr,vul);
up(rt);
}
inline void slove(int u,int v){
int uu=tp[u];int vv=tp[v];
while(uu!=vv){
if(dep[uu]<dep[vv])swap(uu,vv),swap(u,v);
update1(1,1,n,p[uu],p[u],1);
u=fa[uu];uu=tp[u];
}
if(dep[u]>dep[v])swap(u,v);
update1(1,1,n,p[u],p[v],1);
return ;
}
int main(){
scanf("%d",&n);int u,v,k,op;
for(int i=1;i<=n;i++)son[i]=-1;
for(int i=1;i<n;i++)scanf("%d%d",&u,&v),add(u,v),add(v,u);
dfs(1,0,0);dfs1(1,1);
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%d",&op);
if(op==0)scanf("%d%d",&u,&k),update(1,1,n,p[u],p[u]+num[u]-1,k);
else{
scanf("%d",&k);
while(k--)scanf("%d%d",&u,&v),slove(u,v);
printf("%d\n",sum1[1] & 0x7fffffff);
update1(1,1,n,1,n,-1);
}
}
return 0;
}
3589: 动态树
Time Limit: 30 Sec Memory Limit: 512 MBSubmit: 756 Solved: 267
[Submit][Status][Discuss]
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
1 2
2 3
2 4
1 5
3
0 1 1
0 2 3
1 2 3 1 1 4
Sample Output
13
HINT
1 <= n <= 200,000, 1 <= nQ <= 200,000, K = 5.
生成每个树枝的过程是这样的:先在树中随机找一个节点, 然后在这个节点到根的路径上随机选一个节点, 这两个节点就作为树枝的两端.

浙公网安备 33010602011771号