bzoj 4765: 普通计算姬 主席树+替罪羊树思想

题目大意:

给定一棵\(n\)个节点的带权树有根树,设\(sum_p\)表示以点\(p\)为根的这棵子树中所有节点的权
计算姬支持下列两种操作:

  1. 给定两个整数\(u,v\),修改点\(u\)的权值为\(v\)
  2. 给定两个整数\(l,r\),计算\(\sum_{i=l}^rsum_i\)

题解:

表示自己没能想出来...被同桌嘲讽了QAQ...
首先是这道题的数据范围很奇怪,只有10W,这就说明了你有充足的时间来瞎搞
所以我们就瞎搞

如果没有修改操作那么我们直接\(O(n)\)预处理就可以\(O(1)\)询问了
但是我们存在修改操作,而且一次修改朴素是\(O(n)\)
但是我们发现:无论我们要修改多少个数的值,修改的复杂度总是\(O(n)\)

所以我们多攒几次修改在外部维护其对答案的影响,然后凑够了再一块改.
所以我们考虑如何外部维护.
首先我们将问题放到树的dfs序上,

我们发现:
这个问题实际上就是让我们统计一下当前询问的sum区间中有多少区间覆盖了我们修改的点

(注意,每一个sum实际上都代表了dfs序中的一个区间)

所以我们可以一次枚举每一个点,求一下当前的sum区间中有多少区间覆盖了当前枚举的点.
对于这个操作我们可以直接用可持久化线段树瞎搞一下就好了

经过本人的测试:在随机数据下,积累68个修改操作的时候进行修改,跑得最快 !
本题还有一个大坑点 : 答案会炸long long需要开unsigned long long 才能过...

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
inline void read(int &x){
    x=0;char ch;bool flag = false;
    while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
    while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
inline void read(ll &x){
    x=0;char ch;bool flag = false;
    while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
    while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
const int maxn = 100010;
const int lim = 68;
struct Node{
    Node *ch[2];
    ll lazy;
}mem[maxn*50],*it,*null,*root[maxn];
inline void init(){
    it = mem;null = it++;
    null->ch[0] = null->ch[1] = null;
    null->lazy = 0;root[0] = null;
}
Node* insert(Node *rt,int l,int r,int L,int R){
    Node *p = it++;*p = *rt;
    if(L <= l && r <= R){
        p->lazy ++;
        return p;
    }
    int mid = l+r >> 1;
    if(L <= mid) p->ch[0] = insert(p->ch[0],l,mid,L,R);
    if(R >  mid) p->ch[1] = insert(p->ch[1],mid+1,r,L,R);
    return p;
}
ll query(Node *p,int l,int r,int pos){
    if(l == r) return p->lazy;
    int mid = l+r >> 1;
    if(pos <= mid) return query(p->ch[0],l,mid,pos) + p->lazy;
    else return query(p->ch[1],mid+1,r,pos) + p->lazy;
}
struct Edge{
    int to,next;
}G[maxn<<1];
int head[maxn],cnt;
inline void add(int u,int v){
    G[++cnt].to = v;
    G[cnt].next = head[u];
    head[u] = cnt;
}
ll c[maxn],ind[maxn],dfs_clock,oud[maxn];
#define v G[i].to
void dfs(int u,int f){
    ind[u] = ++ dfs_clock;
    for(int i = head[u];i;i=G[i].next){
        if(v == f) continue;
        dfs(v,u);
    }oud[u] = dfs_clock;
}
#undef v
struct save{
    ll bef,id,val;
    save(){}
    save(const ll &a,const ll &b,const ll &c){
        bef = a;id = b;val = c;
    }
}q[maxn];
int q_siz,n;
ull a[maxn],sum[maxn];
inline void rebuild(){
    q_siz = 0;
    memset(a,0,sizeof a);
    for(int i=1;i<=n;++i){
        a[ind[i]] += c[i];
    }
    for(int i=1;i<=n;++i) a[i] += a[i-1];
    for(int i=1;i<=n;++i){
        sum[i] = sum[i-1] + a[oud[i]] - a[ind[i]-1];
    }
}
int main(){
    init();
    int m;read(n);read(m);
    for(int i=1;i<=n;++i) read(c[i]);
    int rt = 0;
    for(int i=1,u,v;i<=n;++i){
        read(u);read(v);
        if(u == 0) rt = v;
        else add(u,v),add(v,u);
    }dfs(rt,0);
    for(int i=1;i<=n;++i){
        root[i] = insert(root[i-1],1,n,ind[i],oud[i]);
    }
    rebuild();
    ll op,u,v;
    while(m--){
        read(op);read(u);read(v);
        if(op == 1){
            q[++q_siz] = save(c[u],u,v);
            c[u] = v;   
            if(q_siz == lim) rebuild();
        }else{
            ll l = u,r = v;
            ull ans = sum[v] - sum[u-1];
            for(ll i=1;i<=q_siz;++i){
                if(q[i].val - q[i].bef > 0) ans += 1ULL*(q[i].val - q[i].bef)*(query(root[r],1,n,ind[q[i].id]) - query(root[l-1],1,n,ind[q[i].id]));
                if(q[i].val - q[i].bef < 0) ans -= 1ULL*(q[i].bef - q[i].val)*(query(root[r],1,n,ind[q[i].id]) - query(root[l-1],1,n,ind[q[i].id]));
            }
            printf("%llu\n",ans);
        }
    }
    getchar();getchar();
    return 0;
}

转载于:https://www.cnblogs.com/Skyminer/p/6561710.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值