[ZJOI2018]历史
Posted Achen
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[ZJOI2018]历史相关的知识,希望对你有一定的参考价值。
zj的题都好神呀
我怎么什么题都做不来呀
看见这题想起了重组病毒,因为知道了是lct,考虑一下和access有什么不可不说的关系
我一开始没考虑清楚,放弃了这个想法,就GG了...
仔细考虑发现,一个城市崛起等于access操作,fight的次数等于虚实边切换的次数
从我往下没有实边到有实边(前提是我父亲到我是实边,也就是实边往下延伸了一条),或者往下的实边变成另一条实边算一次切换
发现发生这样的切换一定是我的子树中某个点access了,那么每个点的贡献可以分开考虑
每个点记录access的次数v和子树中access的总次数sum
考虑每个点子树中的点的access顺序,如果相邻两个access的点不是来自同一个亲儿子(或者自己)的,就会发生一次fight
如过(t=(v[x]或者x的某个亲儿子的sum)*2)>sum[x]+1,这个点的贡献就为(sum[x]-t)*2,否则贡献为sum[x]-1
yy了一下证明
当存在t*2>sum[x]+1的时候,把t这种点一个空一个的放,其他点插空,然后最后有一些t是相邻的这样是一种最优解
当不存在这样的t时,一种构造出相邻全不相同的方法是,在剩下的点不够插空前,按点数的从多到少拿出某一类点,一个空一个的放,相邻两类之间不空,最后一类要是有剩的肯定可以插到第一类点的空中,然后剩下的类的点插空.
定义每个点的重儿子y满足为sum[y]*2>sum[x]+1,这样会有很多优美的性质
每个点最多有一个重儿子
重儿子在树上形成链
轻重重儿子只会在access的时候改变,和普通access不同的是普通access要改变的现在不一定改变
重链上的点贡献不会改变
每个点向上最多logsum[rt]条轻边,因为轻边存在的条件是sum[fa]+1>sum[x]*2
有了这些性质就可以做了,access的时候修改每个会改变的点(log个)的贡献,重链就直接在splay上打上lazytag(打左儿子)
注意没有重儿子也可能我v很大需要按(sum[x]-t)*2计算
1 //Achen
2 #include<algorithm>
3 #include<iostream>
4 #include<cstring>
5 #include<cstdlib>
6 #include<cstdio>
7 #include<cmath>
8 #include<queue>
9 #include<set>
10 #include<map>
11 #define For(i,a,b) for(int i=(a);i<=(b);i++)
12 #define Rep(i,a,b) for(int i=(a);i>=(b);i--)
13 const int N=4e5+7;
14 typedef long long LL;
15 typedef double db;
16 using namespace std;
17 int n,m,no[N];
18 LL v[N],sum[N],A[N],ans;
19
20 template<typename T>void read(T &x) {
21 char ch=getchar(); x=0; T f=1;
22 while(ch!=‘-‘&&(ch<‘0‘||ch>‘9‘)) ch=getchar();
23 if(ch==‘-‘) f=-1,ch=getchar();
24 for(;ch>=‘0‘&&ch<=‘9‘;ch=getchar()) x=x*10+ch-‘0‘; x*=f;
25 }
26
27 int ch[N][2],p[N];
28 #define lc ch[x][0]
29 #define rc ch[x][1]
30 int isroot(int x) {return (ch[p[x]][0]!=x&&ch[p[x]][1]!=x);}
31
32 void rotate(int x) {
33 int y=p[x],z=p[y],l=(x==ch[y][1]),r=l^1;
34 if(!isroot(y)) ch[z][y==ch[z][1]]=x; p[x]=z;
35 ch[y][l]=ch[x][r]; p[ch[x][r]]=y;
36 ch[x][r]=y; p[y]=x;
37 }
38
39 LL lz[N];
40 void down(int x) {
41 if(!lz[x]) return;
42 if(lc) sum[lc]+=lz[x],lz[lc]+=lz[x];
43 if(rc) sum[rc]+=lz[x],lz[rc]+=lz[x];
44 lz[x]=0;
45 }
46
47 void splay(int x) {
48 static int g[N],top=0,tp;
49 for(tp=x;!isroot(tp);tp=p[tp]) g[++top]=tp;
50 g[++top]=tp;
51 while(top) {
52 down(g[top--]);
53 }
54 for(;!isroot(x);rotate(x)) {
55 int y=p[x],z=p[y];
56 if(!isroot(y))
57 ((x==ch[y][1])^(y==ch[z][1]))?rotate(x):rotate(y);
58 }
59 }
60
61 void ADD(int x,LL y) {
62 if(!x) return;
63 sum[x]+=y; lz[x]+=y;
64 }
65
66 int get_min(int x) {
67 while(lc) {
68 down(x);
69 x=lc;
70 }
71 return x;
72 }
73
74 void access(int x,LL V) {
75 v[x]+=V;
76 for(int t=0;x;x=p[t=x]) {
77 splay(x);
78 sum[x]+=V; ADD(lc,V);
79 ans-=A[x];
80 int y=0;
81 if(rc) y=get_min(rc);
82 if(y&&sum[y]*2>sum[x]+1) ans+=(A[x]=2LL*(sum[x]-sum[y]));
83 else rc=0;
84 if(!rc&&t) {
85 y=get_min(t);
86 if(sum[y]*2>sum[x]+1) { ans+=(A[x]=2LL*(sum[x]-sum[y])); rc=t;}
87 }
88 if(!rc) {
89 if(v[x]*2>sum[x]+1) ans+=(A[x]=2LL*(sum[x]-v[x]));
90 else ans+=(A[x]=sum[x]-1);
91 }
92 }
93 }
94
95 int ecnt,fir[N],nxt[N<<1],to[N<<1];
96 void add(int u,int v) {
97 nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v;
98 nxt[++ecnt]=fir[v]; fir[v]=ecnt; to[ecnt]=u;
99 }
100
101 void dfs(int x,int fa) {
102 sum[x]=v[x];
103 for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa) {
104 p[to[i]]=x;
105 dfs(to[i],x);
106 sum[x]+=sum[to[i]];
107 }
108 int mson=0;
109 for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa) {
110 if(sum[to[i]]*2>sum[x]+1) {
111 mson=to[i]; break;
112 }
113 }
114 rc=mson;
115 if(v[x]*2>sum[x]+1) ans+=(A[x]=2LL*(sum[x]-v[x]));
116 else if(mson) ans+=(A[x]=2LL*(sum[x]-sum[mson]));
117 else ans+=(A[x]=sum[x]-1);
118 }
119
120 //#define DEBUG
121 int main() {
122 #ifdef DEBUG
123 freopen("1.in","r",stdin);
124 #endif
125 read(n); read(m);
126 For(i,1,n) read(v[i]);
127 For(i,1,n-1) {
128 int u,v; read(u); read(v);
129 add(u,v);
130 }
131 dfs(1,0);
132 printf("%lld\n",ans);
133 For(ti,1,m) {
134 int x; LL y;
135 read(x); read(y);
136 access(x,y);
137 printf("%lld\n",ans);
138 }
139 return 0;
140 }
以上是关于[ZJOI2018]历史的主要内容,如果未能解决你的问题,请参考以下文章