zoukankan      html  css  js  c++  java
  • [HAOI2015]树上操作

    树剖裸题。

    可以用树状数组,优化成O(nlogn)

    只要找到根的路径上的值的和,考虑一次操作会影响到哪些位置查询的值。

    采用差分。其实是对dfn序的区间加。

    dfs找到dfn,dfn2

    两个树状数组t1,t2;

    操作1:t1.add(dfn[x],z),t1.add(dfn2[x]+1,-z) 子树差分,query的时候,得到的是路径上的单点加的标记。

    操作2:对于在y(x的一个祖先)做子树加z的操作,对x的贡献是:(dep[x]-dep[y]+1)*z = dep[x]*z-(dep[y]-1)*z

    差分一下。t1.add(dfn[y],-(dep[y]-1)*z)     t1.add(dfn2[y]+1,(dep[y]-1)*z)     t2.add(dfn[y],z)  t2.add(dfn2[y]+1,-z)

    这样,查询x的时候,就是t1.query(dfn[x])+dep[x]*t2.query(dfn[x]) ,t1包含了单点加的祖先的值,以及子树加部分的减去部分。t2上是一些标记。扫到一个,乘上dep[x],就把差分实现了。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=100000+5;
    typedef long long ll;
    int dfn[N],dfn2[N],dep[N];
    int a[2*N],tot;
    ll w[N];
    int n,m;
    struct arraytree{    
        ll f[2*N];
        void upda(int x,ll c){for(;x<=2*n;x+=x&(-x))f[x]+=c;}
        ll query(int x){ll ret=0;for(;x;x-=x&(-x))ret+=f[x];return ret;}
    }t1,t2;
    
    struct node{
        int nxt,to;
    }e[2*N];
    int hd[N],cnt;
    void add(int x,int y){
        e[++cnt].nxt=hd[x];
        e[cnt].to=y;
        hd[x]=cnt;
    }
    void dfs(int x,int fa,int d){
        dfn[x]=++tot;
        dep[x]=d;
        for(int i=hd[x];i;i=e[i].nxt){
            int y=e[i].to;
            if(y==fa) continue;
            dfs(y,x,d+1);
        }
        dfn2[x]=tot;
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) scanf("%lld",&w[i]);int x,y;
        for(int i=1;i<n;i++){
            scanf("%d%d",&x,&y);add(x,y);add(y,x);
        }
        dfs(1,0,1);
        //for(int i=1;i<=2*n;i++)cout<<a[i]<<" ";cout<<endl;
        //for(int i=1;i<=n;i++) cout<<i<<" : "<<dfn[i]<<" "<<dfn2[i]<<endl;
        for(int i=1;i<=n;i++){
            t1.upda(dfn[i],w[i]);
            t1.upda(dfn2[i]+1,-w[i]);
        }
        //for(int i=1;i<=n;i++) cout<<i<<" : "<<t1.query(dfn[i])<<endl;
        ll z;int op;
        for(int i=1;i<=m;i++){
            scanf("%d",&op);
            if(op==1){
                scanf("%d%lld",&x,&z);
                t1.upda(dfn[x],z);
                t1.upda(dfn2[x]+1,-z);
            }
            else if(op==2){
                scanf("%d%lld",&x,&z);
                t1.upda(dfn[x],-(dep[x]-1)*z);
                t1.upda(dfn2[x]+1,(dep[x]-1)*z);
                t2.upda(dfn[x],z);
                t2.upda(dfn2[x]+1,-z);
            }
            else{
                scanf("%d",&x);
                printf("%lld
    ",t1.query(dfn[x])+dep[x]*t2.query(dfn[x]));
            }
        }
        return 0;
    }
  • 相关阅读:
    Java类加载器总结
    Java程序编译和运行的过程
    Spring+Struts2+Hibernate整合
    Spring+MyBatis+SpringMVC整合
    声明式事务管理
    Scala sbt 添加国内镜像
    持续天数算法
    idea run shell set user name
    java insert mysql 中文乱码
    Samba服务器 安装
  • 原文地址:https://www.cnblogs.com/Miracevin/p/10106416.html
Copyright © 2011-2022 走看看