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.
生成每个树枝的过程是这样的:先在树中随机找一个节点, 然后在这个节点到根的路径上随机选一个节点, 这两个节点就作为树枝的两端.