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 MB
Submit: 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

Sample Output

13

HINT

 1 <= n <= 200,000, 1 <= nQ <= 200,000, K = 5.


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

 

posted @ 2018-08-21 15:51  wang9897  阅读(145)  评论(0编辑  收藏  举报