zoukankan      html  css  js  c++  java
  • [bzoj4765]普通计算姬

    毒瘤的题。能看出来是离线分块,具体细节还是挺让人难受的,,,主要是坑点太多了


    给定一棵树,两种操作:

    1.修改点权

    2.求编号在$[l,r]$之间的$sum$和。$sum$指子树权值和。

    $n leq 1e5$


    坑一:此题答案可能会爆long long,要用ull。。

    我们最开始想到要对编号分块,维护$sum$。预处理出每次修改点权的时候会对每个块分别作出多少贡献(其实就是每个块里有多少是它的祖先)。这个复杂度是$O(sqrt n)$的。

    坑二:然后我们发现$n sqrt n$的long long数组大概正好256MB的样子,要卡一卡。

    块内的自然$O(1)$出答案,块外的由于修改时不能具体到一个点所以要单求。显然对于单求来说最优的是用树状数组$O(logn)$出解。

    然而我们上限有$sqrt n$个点要求解,于是询问变成了$O(sqrt n logn)$的了。

    坑三:这个复杂度会被卡掉。可能会说块不开$sqrt n$的,然而最优的$sqrt{frac n{log_2n}}$,均摊每次操作也要大概$2000$左右复杂度。

    于是我们发现修改快,询问慢。我们能不能牺牲一些修改时间使询问更加快速呢?

    然后我们再对dfs序使用分块,维护权值前缀和。这样修改时复杂度仍然是$O(sqrt n)$,只不过多了一倍常数在维护这个前缀和上。询问的时候,我们要求的是一个区间,只要知道两个单点,相减就行了。查询单点是$O(1)$的,有$sqrt n$个要查询,这样询问也变成$O(sqrt n)$的了。

    于是就顺利解决了!真的是很妙很坑很毒瘤的一道题,,

    P.S. 分块好久没写,想回去看一下以前数颜色的代码。然后发现我的bel数组一直是0?然后发现这样l和r的所在块一直一样,就一直在做暴力,然后我就是暴力A的?mmp,我把bel数组赋上值,然后寻思能快点,然后,,WA了。mmp


    #include<cstdio>
    #include<cctype>
    #include<cmath>
    #include<iostream>
    using namespace std;
    const int N=100001;
    const int M=318;
    typedef unsigned long long ll;
    inline int read(){
        int r=0,c=getchar();
        while(!isdigit(c))c=getchar();
        while(isdigit(c))
        r=r*10+c-'0',c=getchar();
        return r;
    }
    struct Edge{
        int to,nxt;
    }e[N*2];
    int head[N],cnt=1;
    void add(int u,int v){
        e[cnt]=(Edge){v,head[u]};
        head[u]=cnt++;
        e[cnt]=(Edge){u,head[v]};
        head[v]=cnt++;
    }
    int n,m,tot,dc=-1;
    int in[N],out[N],bel[N];
    ll s[N],sum[M],S[N],Lz[M];
    ll c[N][M],val[N],bk;
    void dfs(int u,int fa){
        in[u]=++dc;S[dc]=S[dc-1]+val[u];
        for(int i=1;i<=tot;i++)
        c[u][i]=c[fa][i];
        c[u][bel[u]]++;
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].to;
            if(v==fa)continue;
            dfs(v,u);s[u]+=s[v];
        }
        out[u]=dc;
    }
    void modify(int u,ll v){
        ll dt=v-val[u];val[u]=v;
        for(int i=1;i<=tot;i++)
        sum[i]+=c[u][i]*dt;
        int l=in[u],r=n;
        if(bel[l]==bel[r]){
            for(int i=l;i<=r;i++)
            S[i]+=dt;
            return;
        }
        for(int i=bel[l]+1;i<bel[r];i++)
        Lz[i]+=dt;
        for(int i=l;i<=bel[l]*bk;i++)
        S[i]+=dt;
        for(int i=(bel[r]-1)*bk+1;i<=r;i++)
        S[i]+=dt;
        int ans=0;
    }
    ll ask(int x){
        return S[x]+Lz[bel[x]];
    }
    void query(int l,int r){
        ll ans=0;
        if(bel[l]==bel[r]){
            for(int i=l;i<=r;i++)
            ans+=ask(out[i])-ask(in[i]-1);
            printf("%llu
    ",ans);
            return;
        }
        for(int i=bel[l]+1;i<bel[r];i++)
        ans+=sum[i];
        for(int i=l;i<=bel[l]*bk;i++)
        ans+=ask(out[i])-ask(in[i]-1);
        for(int i=(bel[r]-1)*bk+1;i<=r;i++)
        ans+=ask(out[i])-ask(in[i]-1);
        printf("%llu
    ",ans);
    }
    void init(){
        n=read(),m=read();
        bk=sqrt(n);tot=n%bk==0?n/bk:n/bk+1;
        for(int i=1;i<=n;i++)
        val[i]=s[i]=read(),bel[i]=(i-1)/bk+1; 
        for(int i=1;i<=n;i++)
        add(read(),read());
        dfs(0,0);
        for(int i=1;i<=n;i++)
        sum[bel[i]]+=s[i];
    }
    void solve(){
        while(m--){
            int opt=read(),x=read(),y=read();
            if(opt==1)modify(x,y);
            else query(x,y);
        }
    }
    int main(){
        init();
        solve();
    }
  • 相关阅读:
    Spring AOP应用场景你还不知道?这篇一定要看!
    解决 Failed to start LSB: Bring up/down networking 问题
    查出undefined symbol项命令
    将当前目录加入库环境变量
    Fortran代码生成so库
    Java调用Fortran生成so库报“libifport.so.5: 无法打开共享对象文件”错误解决方法
    HBase过滤器(转载)
    HBase设计规范(转载)
    spark(2.1.0) 操作hbase(1.0.2)
    zookeeper搭建
  • 原文地址:https://www.cnblogs.com/orzzz/p/8604224.html
Copyright © 2011-2022 走看看