• BZOJ3730 震波 【动态点分治】*


    BZOJ3730 震波


    Description

    在一片土地上有N个城市,通过N-1条无向边互相连接,形成一棵树的结构,相邻两个城市的距离为1,其中第i个城市的价值为value[i]。
    不幸的是,这片土地常常发生地震,并且随着时代的发展,城市的价值也往往会发生变动。
    接下来你需要在线处理M次操作:
    0 x k 表示发生了一次地震,震中城市为x,影响范围为k,所有与x距离不超过k的城市都将受到影响,该次地震造成的经济损失为所有受影响城市的价值和。
    1 x y 表示第x个城市的价值变成了y。
    为了体现程序的在线性,操作中的x、y、k都需要异或你程序上一次的输出来解密,如果之前没有输出,则默认上一次的输出为0。

    Input

    第一行包含两个正整数N和M。
    第二行包含N个正整数,第i个数表示value[i]
    接下来N-1行,每行包含两个正整数u、v,表示u和v之间有一条无向边。
    接下来M行,每行包含三个数,表示M次操作。

    Output

    包含若干行,对于每个询问输出一行一个正整数表示答案。

    Sample Input

    8 1
    1 10 100 1000 10000 100000 1000000 10000000
    1 2
    1 3
    2 4
    2 5
    3 6
    3 7
    3 8
    0 3 1

    Sample Output

    11100101

    HINT

    1<=N,M<=100000
    1<=u,v,x<=N
    1<=value[i],y<=10000
    0<=k<=N-1


    动态点分治

    噗噗噗恶习死了

    首先我们是肯定需要求出两点间的路径的,然后就可以上倍增LCA的板子
    然后开始考虑怎么维护答案

    如果不考虑修改
    每次询问的答案可以用怎样的形式来统计呢?
    因为我们维护一个点的字树信息很方便,所以就可以先用树状数组维护一下一个子树里的信息,以距离为下标,点权直接单点加上就行了

    现在考虑怎么统计子树外边的贡献,首先我们可以跳到点分树上的prt节点上,然后求出prt到当前节点u的距离len,用查询距离k减去len,查询在prt的子树里边有多少距离小于等于k-len的点就可以了,但是我们发现在u的子树内的一部分节点会被重复计算,所以我们需要另开一个树状数组来维护重复的贡献,在这个树状数组里边只记录在u的子树中的额外贡献

    现在考虑吧修改加上,我们从修改节点x这个点开始,不停向prt跳,然后我们这里可以把每一个原来是val[x]的贡献换成y的贡献,简而言之就是加上y-val[x],然后跟查询差不多,只不过反向维护一下就好了

    然后细节一大堆,主要就是跳prt维护的细节


    #include<bits/stdc++.h>
    using namespace std;
    #define N 500010
    inline int read(){
        int ans=0,w=1;char c=getchar();
        while(!isdigit(c)&&c!='-')c=getchar();
        if(c=='-')w=-1,c=getchar();
        while(isdigit(c))ans=(ans<<1)+(ans<<3)+c-'0',c=getchar();
        return ans*w;
    }
    int n,m,lastans=0,val[N],Log[N];
    struct Edge{
        int v,next;
        Edge(){}
        Edge(int v,int next):v(v),next(next){}
    }E[N<<1];
    int head[N],tot=0;
    void add(int u,int v){
        E[++tot]=Edge(v,head[u]);
        head[u]=tot;
    }
    int dep[N]={0},Fa[N][21];
    void getFa(int u,int fa){
        Fa[u][0]=fa;
        dep[u]=dep[fa]+1;
        for(int i=1;i<=20;i++)Fa[u][i]=Fa[Fa[u][i-1]][i-1];
        for(int i=head[u];i;i=E[i].next){
            int v=E[i].v;
            if(v==fa)continue;
            getFa(v,u);
        }
    }
    int Lca(int x,int y){
        if(dep[x]<dep[y])swap(x,y);
        int t=dep[x]-dep[y],k=Log[n]+1;
        for(int i=0;i<=k;i++)
        if(t&(1<<i))x=Fa[x][i];
        if(x==y)return x;
        while(Fa[x][0]!=Fa[y][0]){
            if(Fa[x][k]!=Fa[y][k]){
                x=Fa[x][k];
                y=Fa[y][k];
            }
            k--;
        }
        return Fa[x][0];
    }
    int getdis(int x,int y){return dep[x]+dep[y]-dep[Lca(x,y)]*2;}
    vector<int> t[2][N];
    void modify(int typ,int pos,int x,int vl){
        if(!x)return;
        int up=t[typ][pos].size();
        while(x<up){
            t[typ][pos][x]+=vl;
            x+=x&(-x);
        }
    }
    int query(int typ,int pos,int x){
        int res=0;
        x=min(x,(int)(t[typ][pos].size()-1));
        while(x){
            res+=t[typ][pos][x];
            x-=x&(-x);
        }
        return res;
    }
    int siz[N],F[N],rt,siz_tree;
    int prt[N];bool vis[N];
    void getroot(int u,int fa){
        siz[u]=1;F[u]=0;
        for(int i=head[u];i;i=E[i].next){
            int v=E[i].v;
            if(v==fa||vis[v])continue;
            getroot(v,u);
            siz[u]+=siz[v];
            F[u]=max(F[u],siz[v]);
        }
        F[u]=max(F[u],siz_tree-siz[u]);
        if(F[u]<F[rt])rt=u;
    }
    void divide(int u,int fa){
        prt[u]=fa;vis[u]=1;
        t[0][u].resize(siz_tree+1);
        t[1][u].resize(siz_tree+1);
        for(int i=head[u];i;i=E[i].next){
            int v=E[i].v;
            if(vis[v])continue;
            F[rt=0]=siz_tree=siz[v];
            getroot(v,u);
            divide(rt,u);
        }
    }
    void modify(int u,int vl){
        for(int i=u;i;i=prt[i]){
            modify(0,i,getdis(u,i),vl);
            if(!prt[i])break;
            modify(1,i,getdis(u,prt[i]),vl);
        }
    }
    int query(int u,int vl){
        int res=query(0,u,vl)+val[u];
        for(int i=prt[u],last=u;i;last=i,i=prt[i]){
            int d=vl-getdis(u,i);
            if(d<0)continue;
            res+=query(0,i,d)-query(1,last,d)+val[i];
        }
        return res;
    }
    void work(){
        F[rt=0]=siz_tree=n;
        getroot(1,0);
        divide(rt,0);
    }
    int main(){
        freopen("bzoj3730.in","r",stdin);
        Log[0]=-1;for(int i=1;i<N;i++)Log[i]=Log[i>>1]+1;
        n=read();m=read();
        for(int i=1;i<=n;i++)val[i]=read();
        for(int i=1;i<n;i++){
            int u=read(),v=read();
            add(u,v);
            add(v,u);
        }
        getFa(1,0);
        work();
        for(int i=1;i<=n;i++)modify(i,val[i]);
        for(int i=1;i<=m;i++){
            int op=read(),x=read(),y=read();
            x^=lastans,y^=lastans;
            if(op)modify(x,y-val[x]),val[x]=y;
            else printf("%d
    ",lastans=query(x,y));
        }
        return 0;
    }
  • 相关阅读:
    SQLServer2008新建链接服务器for Oracle
    提示Can't load package:dclite70.bpl解决方法
    android实现自动升级并安装打开
    【转】Ubuntu 14.04.3上配置并成功编译Android 6.0 r1源码
    Android日志打印类LogUtils,能够定位到类名,方法名以及出现错误的行数并保存日志文件
    Linux 自定义命令
    CentOS时间的查看与修改
    Linux expect自动登录ssh,ftp
    ★Linux命令行操作技巧(作为服务器端)
    ★Linux桌面系统技巧(作为客户端)
  • 原文地址:https://www.cnblogs.com/dream-maker-yk/p/9676299.html
Copyright © 2020-2023  润新知