[虚树][树状数组][lca] Jzoj P5908 开荒

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
保证数据合法

 

 

题解

  • 题目大意,有q次操作,询问操作是求k个点两两到lca所经过点的点权和,修改操作就是修改一个点的点权
  • 首先,求距离可以用树状数组按照dfs序加入,类似差分约束一样求
  • 当然修改也可以
  • 那么现在问题就是直接暴力两两lca做显然不行,要找一种更快的做法
  • 可以建一棵虚树
  • 然后对于虚树上的非父亲节点,直接求出他到父亲的点权和(不包括父亲),而对于父亲则直接加上自己的点权即可

代码

 1 #include <cstdio> 
 2 #include <iostream>
 3 #include <algorithm>
 4 #include <cstring>
 5 using namespace std;
 6 const int N=2e5+10;
 7 int n,Q,tot,num,cnt,p,w,head[N],v[N],be[N],en[N],fa[N],d[N],dep[N],f[N][20],k[N],q[N];
 8 long long sz[N],ans;
 9 struct edge {int to,from;}e[N];
10 void dfs(int x,int pre)
11 {
12     f[x][0]=fa[x]=pre,be[x]=++tot,d[x]=d[fa[x]]+1,dep[x]=dep[pre]+1;
13     for (int i=head[x];i;i=e[i].from) if (e[i].to!=pre) dfs(e[i].to,x);
14     en[x]=tot;
15 }
16 bool cmp(int x,int y) { return be[x]<be[y]; }
17 int getlca(int x,int y)
18 {
19     if (dep[x]<dep[y]) swap(x,y);
20     for (int i=18;i>=0;i--) if (dep[f[x][i]]>=dep[y]) x=f[x][i];
21     if (x==y) return x;
22     for (int i=18;i>=0;i--) if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
23     return f[x][0];
24 }
25 void add(int x,int y) { for (;x<=n;x+=x&-x) sz[x]+=y; }
26 long long getsum(int x)
27 {
28     long long v=0;
29     for (;x;x-=x&-x) v+=sz[x];
30     return v;
31 }
32 void insert(int x,int y) { e[++cnt].to=y; e[cnt].from=head[x]; head[x]=cnt; }
33 int main()
34 {
35     //freopen("kaihuang.in","r",stdin),freopen("kaihuang.out","w",stdout);
36     scanf("%d%d",&n,&Q);
37     for (int i=1;i<=n;i++) scanf("%d",&v[i]);
38     for (int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),insert(x,y),insert(y,x);
39     dfs(1,0);
40     for (int j=1;j<=19;j++) for (int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1];
41     for (int i=1;i<=n;i++) add(be[i],v[i]),add(en[i]+1,-v[i]);
42     while (Q--)
43     {
44         scanf("\n");
45         char ch=getchar(); int x,y;
46         if (ch=='C')
47         {
48             scanf("%d%d",&x,&y),y-=v[x],v[x]+=y;
49             add(be[x],y),add(en[x]+1,-y);
50         }
51         else 
52         {
53             num=1,p=0; scanf("%d",&k[num]); while (k[num]!=0) scanf("%d",&k[++num]); num--;
54             sort(k+1,k+num+1,cmp);
55             w=num; for (int i=1;i<=w-1;i++) k[++num]=getlca(k[i],k[i+1]);
56             sort(k+1,k+num+1,cmp),num=unique(k+1,k+num+1)-k-1;
57             ans=0;
58             for (int i=1;i<=num;i++)
59             {
60                 for (;p&&en[q[p]]<be[k[i]];) p--;
61                 ans+=p?getsum(be[k[i]])-getsum(be[q[p]]):v[k[i]];
62                 q[++p]=k[i];
63             }
64             printf("%lld\n",ans);
65         }
66     }
67 }

 

posted @ 2018-10-17 10:35  BEYang_Z  阅读(212)  评论(0编辑  收藏  举报