BZOJ 4765 普通计算姬 dfs序+分块+树状数组+好题!!!

真是道好题。。。感到灵魂的升华。。。


 

按dfs序建树状数组,拿前缀和去求解散块;

按点的标号分块,分成一个个区间,记录区间子树和 的 总和。。。

具体地,需要记录每个点u修改后,对每一个块i的贡献,记为t[u][i]

计算思路:dfs时,每到一个新的点,就让++c[其所在块],为了记录每个块中的点出现过几次,就相当于记录这个点 被每一块中的点 包含了几次 , 然后for一遍,记录t[u][i]=c[i]

当修改一个点时,这个块的和+=这个点u对块i的贡献*这个点的变化量,即sum[i]+=t[u][i]*(v-a[u]);

#include<cstdio>
#include<iostream>
#include<cmath>
#define ll unsigned long long
#define R register ll
using namespace std;
const int N=100010;
inline ll g() {
    R ret=0,fix=1; register char ch; while(!isdigit(ch=getchar())) fix=ch=='-'?-1:fix;
    do ret=ret*10+(ch^48); while(isdigit(ch=getchar())); return ret*fix;
}
int n,m,q,T,cnt,num,rt;
int vr[N<<1],nxt[N<<1],a[N],fir[N],dfn[N],l[N],r[N],c[320],t[N][320],pos[N];
ll w[N],sum[320];
inline void add(int u,int v) {vr[++cnt]=v,nxt[cnt]=fir[u],fir[u]=cnt;}
inline int lbt(int x) {return x&-x;}
inline void inc(int pos,ll d) {for(;pos<=n;pos+=lbt(pos)) w[pos]+=d;}
inline ll query(int pos) { R ret=0; 
    for(;pos;pos-=lbt(pos)) 
    ret+=w[pos]; return ret;
}
void dfs(int u) { l[u]=++num,++c[pos[u]]; inc(num,a[u]);
    for(R i=1;i<=pos[n];++i) t[u][i]=c[i];
    for(R i=fir[u];i;i=nxt[i]) { R v=vr[i];
        if(l[v]) continue; dfs(v);
    } r[u]=num,--c[pos[u]];
}
signed main() {
    n=g(),q=g(); T=sqrt(n); m=(n%T)?n/T+1:n/T;
    for(R i=1;i<=n;++i) a[i]=g(); for(R i=1;i<=n;++i) pos[i]=(i-1)/T+1;
    for(R i=1,u,v;i<=n;++i) {u=g(),v=g(); if(!u) rt=v; else add(u,v),add(v,u);}
    dfs(rt); 
    for(R i=1;i<=pos[n];++i) for(R j=(i-1)*T+1,lim=min(i*T,(ll)n);j<=lim;++j) sum[i]+=query(r[j])-query(l[j]-1);
    for(R i=1;i<=q;++i) {
        R k=g(),u=g(),v=g();
        if(k&1) {
            inc(l[u],v-a[u]);
            for(R i=1;i<=pos[n];++i) sum[i]+=1ull*t[u][i]*(v-a[u]);
            a[u]=v;
        } else { R ret=0;
            if(pos[u]==pos[v]) for(R i=u;i<=v;++i) ret+=query(r[i])-query(l[i]-1);
            else {
                for(R i=u;i<=pos[u]*T;++i) ret+=query(r[i])-query(l[i]-1);
                for(R i=(pos[v]-1)*T+1;i<=v;++i) ret+=query(r[i])-query(l[i]-1);
            } for(R i=pos[u]+1;i<pos[v];++i) ret+=sum[i]; printf("%llu\n",ret);        
        }
    }
}

这题真神仙。。2019.04.23

posted @ 2019-04-23 18:34  LuitaryiJack  阅读(148)  评论(0编辑  收藏  举报