[虚树][树状数组]JZOJ 5908 开荒

Description

题目背景:
尊者神高达作为一个萌新,在升级路上死亡无数次后被一只大黄叽带回了师门。他加入师门后发现有无穷无尽的师兄弟姐妹,这几天新副本开了,尊者神高达的师门作为一个 pve师门,于是他们决定组织一起去开荒。

题目描述:
师门可以看做以 1 为根的一棵树,师门中的每一个人都有一定的装备分数。一共会有 q 个事件。每个事件可能是一次开荒,也可能是因为开荒出了好装备而导致一个人的装分出现了变化。对于一次开荒,会有 k 个人组织,由于师门的号召力很强,所以所有在组织者中任意两个人简单路径上的人都会参加。
 

Input

第一行 n ,q;
接下来 1 行 n 个数,代表每个人的分值;
接下来 n-1 行 u,v 代表一条边
接下来 q 行
Q 代表询问,接下来 k 个数代表组织的人数,读入为 0时停止读入。
C 代表修改,输入 x,w 代表将 x 的分值变为 w

Output

共 Q 的数量行,为开荒的人的总分值
 

Sample Input

4 4
10 5 2 2
1 2
2 3
2 4
Q 3 4 0
C 3 200
Q 3 4 0
Q 1 4 0

Sample Output

9
207
17

样例解释:
第一次询问,参加的人有 2,3,4 5+2+2=9
第一次修改,权值为 10 5 200 2
第二次询问,参加的人有 2,3,4 5+200+2=207
第三次询问,参加的人有 1,2,4 10+5+2=17
 

Data Constraint

数据范围:
20%的数据 n<=10000,q<=500;
另外 20%的数据 k=2
另外 20%的数据 没有修改操作
所有数据 n,q<=100000,所有询问 k 的和<=1000000
保证数据合法

分析

由于询问点少,我们考虑建虚树(假

然后用树状数组维护关键节点的和就行了

 

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#define lowbit(x) x&-x
using namespace std;
typedef long long ll;
const int N=1e5+10;
struct Edge {
    int u,v,nx;
}g[2*N];
int cnt,list[N];
ll c[N],w[N];
int st[N],ed[N],tme;
int dep[N],f[N][21];
int a[2*N],acnt;
int n,q;

void Addedge(int u,int v) {
    g[++cnt].u=u;g[cnt].v=v;g[cnt].nx=list[u];list[u]=cnt;
}

int LCA(int u,int v) {
    if (dep[u]<dep[v]) swap(u,v);
    for (int i=20;i>=0;i--)
        if (dep[f[u][i]]>=dep[v]) u=f[u][i];
    if (u==v) return u;
    for (int i=20;i>=0;i--)
        if (f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];
    return f[u][0];
}

void Dfs(int u,int fa) {
    st[u]=++tme;dep[u]=dep[fa]+1;
    for (int i=1;i<=20;i++) f[u][i]=f[f[u][i-1]][i-1];
    for (int i=list[u];i;i=g[i].nx)
        if (g[i].v!=fa) {
            f[g[i].v][0]=u;
            Dfs(g[i].v,u);
        }
    ed[u]=tme;
}

void Add(int x,ll y) {
    for (int i=x;i<=n;i+=lowbit(i)) c[i]+=y;
}

ll Get_Sum(int x) {
    ll ans=0;
    for (int i=x;i;i-=lowbit(i)) ans+=c[i];
    return ans;
}

bool Cmp(int a,int b) {
    return st[a]<st[b];
}

int main() {
    freopen("kaihuang.in","r",stdin);
    freopen("kaihuang.out","w",stdout);
    scanf("%d%d",&n,&q);
    for (int i=1;i<=n;i++) scanf("%lld",&w[i]);
    for (int i=1;i<n;i++) {
        int u,v;
        scanf("%d%d",&u,&v);
        Addedge(u,v);Addedge(v,u);
    }
    Dfs(1,0);
    for (int i=1;i<=n;i++) Add(st[i],w[i]),Add(ed[i]+1,-w[i]);
    for (int i=1;i<=q;i++) {
        char c;
        do {
            scanf("%c",&c);
        }
        while (c!='C'&&c!='Q');
        if (c=='C') {
            int a;ll b;
            scanf("%d%lld",&a,&b);
            Add(st[a],b-w[a]);Add(ed[a]+1,w[a]-b);
            w[a]=b;
            continue;
        }
        acnt=0;
        do {
            acnt++;
            scanf("%d",&a[acnt]);
        }
        while (a[acnt]!=0);
        acnt--;
        sort(a+1,a+acnt+1,Cmp);
        int fakeacnt=acnt;
        for (int i=1;i<fakeacnt;i++)
            a[++acnt]=LCA(a[i],a[i+1]);
        sort(a+1,a+acnt+1,Cmp);acnt=unique(a+1,a+acnt+1)-a-1;
        stack<int> stk;
        ll ans=0;
        while (!stk.empty()) stk.pop();
        for (int i=1;i<=acnt;i++) {
            for (;!stk.empty()&&ed[stk.top()]<st[a[i]];stk.pop());
            ans+=!stk.empty()?Get_Sum(st[a[i]])-Get_Sum(st[stk.top()]):w[a[i]];
            stk.push(a[i]);
        }
        printf("%lld\n",ans);
    }
}
View Code

 

posted @ 2018-10-18 22:27  Vagari  阅读(243)  评论(0编辑  收藏  举报