最近莫名其妙地喜欢上了用这种格式写各省省选的全套题解= =
今年浙江省选的出题人是算法竞赛界传说级人物陈立杰,看样子他的出题风格很有特点……ABC三题难度是严格递减的,感觉如果在做第一题的时候被卡住的话恐怕连想死的心都有了……
那么我们先从最难的一题开始……= =
给定一棵N个结点的有正的边权、初始点权为0的无根树,进行M次操作,每次将一个点u的权值增加e($0 \leq |e| \leq 1000$),保证任意时刻点权非负。你的任务是在每次操作后找到一个带权重心u,使得所有点到重心的距离与点权的乘积之和最小(即最小化$\sum_{v} dist(u, v) × val_v,并输出这个最小的值。
N, M均不超过${10}^5$.保证每个点的度数均不超过20.
分析.
首先我们假设每次操作过后我们可以快速地在线查询以任意一个点为关键点得到的权值和,那么在这种情况下如何求出最小权值?
为了表达方便,我们不妨设当前以点u为关键点求得的权值和为$S_u$,那么我们不难发现这样一个性质:在树上任意两点a, b之间的路径上,$S_u$构成了一个存在极小值的单峰函数。证明也很简单:考虑路径上任意一条边e,设e两端点分别为s, t,两端连接的点集分别为S, T,边权为e.v。则关键点从s走到t的过程中权值和的变化量:$$\Delta = S_t - S_s = (\sum_{u \in S} val_u - \sum_{v \in T} val_v) * e.v.$$ 而在转移的过程中,点t和它的不在链上的后代结点都将从T集合转移到S集合,即 $(\sum_{u \in S} val_u - \sum_{v \in T} val_v)$ 是单调递增的,又由题意得知边权都是正整数,因此函数 $\Delta = S_t - S_s = (\sum_{t \in S} val_u- \sum_{v \in T} val_v) * e.v.$ 的零点区间一定唯一(由于$\Delta$是离散函数,这里“零点区间”指的是左右两侧函数值正负性相反的区间),且左负右正。由于$\Delta$表示的是S函数的增量,那么$\Delta$的零点区间唯一且左负右正就证明了S是存在极小值的单峰函数。
那么我们设点c为我们要求的一个带权重心。考虑树上任意一点u和它到c之间的路径,由于u的S函数取最小值,又由路径上S函数值的单峰性,我们可以证明在从u到c的路径上S值是单调递增的,而相邻两点S值相同当且仅当这两点的S值均为$S_u$,即最小值。最后这点结论可以由“零点区间连续”自然地得出。
有了这条性质,查询最小权值就好办了。我们可以在树上任取一点u将树有根化,判断它的各邻接点的S值是否小于$S_u$。若存在一点v使得$S_v < S_u$,那么根据上面的结论,我们知道答案一定在v所在的子树中,递归查询即可。若不存在这样的点v,那么答案一定是$S_u$。
听起来很爽对不对?然而,如果我们每次在树上“任取一点”,最坏情况下递归的深度可以达到$O(N)$级别,时间复杂度退化得很严重。怎么办呢?我们可以在树上找到一点u,使得以u为根最大的子树的规模最小化(一般称u为这棵树的重心)。那么这样每棵子树的规模都不会超过原树规模的1/2,那么不难证明此时递归查询的深度就成了$O(\log N)$。
再来考虑开头我们假设的我们已经会了的操作——在线查询任意一个$S_u$。
考虑我们刚才建立的重心分治结构。对点v进行修改时,我们可以花费$O(\log N)$的时间更新v所在的每一层分治结构的重心维护的答案(即在分治u维护的答案中增加$dist(u, v) * \Delta val_v$),并记录每层分治结构中的结点对上一层分治维护的答案的贡献。在对点v查询时,先将答案设为v分治中维护的答案,然后向上移动累加答案:在从分治current向它的上一层分治parent移动时,在parent维护的答案中减去current对它的贡献得到$\delta S$,将得到的结果临时当做点v的后代累加进答案。即$Ans = lastAns + \delta S + (Sum_{parent} - Sum_{current}) * dist(parent, v) $,其中$Sum_t$表示t维护的分治结构中所有点权的平凡加和。这样,我们就会做这道题了。
如果我们用倍增LCA法求dist,时间复杂度为$O((N+M) \log^3 N) $,可能有些卡常数。考虑到操作不会改变原树的结构,我们可以在$O(N \log N)$的时间内预处理后通过ST表维护DFS序列来求LCA,总时间复杂度$O((N+M) \log^2 N)$.
代码.
2 /**********************By Asm.Def-Wu Jiaxin*****************************/
3 /***********************************************************************/
4 #include <cstdio>
5 #include <cstring>
6 #include <cstdlib>
7 #include <ctime>
8 #include <cctype>
9 #include <algorithm>
10 using namespace std;
11 #define SetFile(x) ( freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout) );
12 #define UseFREAD
13 #ifdef UseFREAD
14 #define getc() *(file_ptr++)
15 #define FreadLenth 5000000
16 char CHARPOOL[FreadLenth], *file_ptr = CHARPOOL;
17 #else
18 #define getc() getchar()
19 #endif
20 #ifdef DEBUG
21 #include <sys/timeb.h>
22 timeb SysTp;
23 #endif
24 template<class T>inline void getd(T &x){
25 char ch = getc();bool neg = false;
26 while(!isdigit(ch) && ch != '-')ch = getc();
27 if(ch == '-')ch = getc(), neg = true;
28 x = ch - '0';
29 while(isdigit(ch = getc()))x = x * 10 - '0' + ch;
30 if(neg)x = -x;
31 }
32 /***********************************************************************/
33 const int maxn = 100005;
34 typedef long long LL;
35
36 int N, Q, lg2[maxn << 1], dfs_iter;
37
38 struct Node{
39 Node *adj[22], *p, *Brid;
40 int w[22], adj_d, dep, dis, id;//用于距离查询
41 int size;//临时变量
42 LL Val, Sum, Cont, Ans;//树分治
43 int AnsTag;
44 bool tag;
45 inline void Link(Node *t, int v){
46 w[adj_d] = v;
47 adj[adj_d++] = t;
48 }
49 void dfs();void dfs2();
50 Node *dp(int, int &);
51 LL Query();
52 inline void GetAns();
53 }T[maxn], *ST[18][maxn<<1], *Root;
54
55 void Node::dfs(){
56 (*ST)[id = dfs_iter++] = this;
57 int i;Node *to;
58 for(i = 0;i < adj_d;++i)if((to = adj[i]) != p){
59 to->p = this, to->dep = dep + 1, to->dis = dis + w[i];
60 to->dfs();
61 (*ST)[dfs_iter++] = this;
62 }
63 }
64
65 inline Node * Cmp(Node *a, Node *b){return a->dep > b->dep ? b : a;}
66
67 inline void Build_ST(){
68 int i = 2, j = 1, t = 4;
69 while(i <= dfs_iter){
70 if(i == t)++j, t <<= 1;
71 lg2[i++] = j;
72 }
73 for(i = 1, t = 2;t <= dfs_iter;++i, t <<= 1)for(j = 0;j + t <= dfs_iter;++j)
74 ST[i][j] = Cmp(ST[i-1][j], ST[i-1][j+(t >> 1)]);
75 }
76
77 inline int dist(Node *a, Node *b){
78 int u = a->id, v = b->id;if(v < u)swap(u, v);
79 int len = v - u + 1, lg = lg2[len];
80 return a->dis + b->dis - (Cmp(ST[lg][u], ST[lg][v+1-(1 << lg)])->dis << 1);
81 }
82
83 void Node::dfs2(){
84 size = 1;
85 int i;Node *to;
86 for(i = 0;i < adj_d;++i)if(!(to = adj[i])->tag && to != p){
87 to->p = this;
88 to->dfs2();
89 size += to->size;
90 }
91 }
92
93 Node * Node::dp(int psize, int &Maxsize){
94 Node *ans = this, *to, *t;Maxsize = psize;
95 int mx, i;
96 for(i = 0;i < adj_d;++i)if((!(to = adj[i])->tag) && to != p)
97 Maxsize = max(Maxsize, to->size);
98 psize += size;
99 for(i = 0;i < adj_d;++i)if((!(to = adj[i])->tag) && to != p){
100 t = to->dp(psize - to->size, mx);
101 if(mx < Maxsize)ans = t, Maxsize = mx;
102 }
103 return ans;
104 }
105
106 Node *Build_DC(Node *t){
107 int s;t->p = NULL;
108 t->dfs2();
109 Node *root = t->dp(0, s), *to;
110 root->tag = true;root->Brid = t;
111 for(int i = 0;i < root->adj_d;++i){
112 if((to = root->adj[i])->tag)swap(root->adj[i--], root->adj[--root->adj_d]);
113 else{
114 root->adj[i] = Build_DC(to);
115 root->adj[i]->p = root;
116 }
117 }
118 return root;
119 }
120
121 inline void Modify(Node *v, int d){
122 v->Sum += d;
123 Node *t = v;
124 LL cont;
125 while(t != Root){
126 LL &s_cont = t->Cont;
127 t = t->p;
128 t->Sum += d;
129 cont = (LL)dist(v, t) * d;
130 t->Val += cont;s_cont += cont;
131 }
132 }
133
134 inline void Node::GetAns(){
135 if(AnsTag == Q)return;//避免一次操作后重复查询
136 Ans = Val;
137 Node *t = p;
138 LL sum_d = Sum, cont = Cont, diff, dsum;
139 while(t){
140 diff = t->Val - cont;dsum = t->Sum - sum_d;
141 if(dsum)Ans += diff + dsum * dist(t, this);
142 sum_d = t->Sum, cont = t->Cont;
143 t = t->p;
144 }
145 AnsTag = Q;
146 }
147
148 LL Node::Query(){
149 GetAns();
150 int i;Node *to;
151 for(i = 0;i < adj_d;++i){
152 (to = adj[i])->Brid->GetAns();
153 if(to->Brid->Ans < Ans)return to->Query();
154 }
155 return Ans;
156 }
157
158 inline void init(){
159 int i, a, b, c;
160 for(i = 1;i < N;++i){
161 getd(a), getd(b), getd(c);
162 T[a].Link(T + b, c);
163 T[b].Link(T + a, c);
164 }
165 T[1].dfs();
166 Build_ST();
167 (Root = Build_DC(T + 1))->p = 0x0;
168 }
169
170 int main(){
171
172 #ifdef DEBUG
173 freopen("test.txt", "r", stdin);ftime(&SysTp);
174 size_t Begin_sec = SysTp.time, Begin_mill = SysTp.millitm;
175 #elif !defined ONLINE_JUDGE
176 SetFile(zjoi15_tree);
177 #endif
178 #ifdef UseFREAD
179 fread(file_ptr, 1, FreadLenth, stdin);
180 #endif
181
182 int u, e;
183 getd(N), getd(Q);
184
185 init();
186
187 while(Q){
188 getd(u), getd(e);
189 Modify(T + u, e);
190 printf("%lld\n", Root->Query());
191 --Q;
192 }
193
194 #ifdef DEBUG
195 ftime(&SysTp);
196 printf("\n%.3lf sec \n", (SysTp.time - Begin_sec) + (SysTp.millitm - Begin_mill) / 1000.0);
197 #endif
198 return 0;
199 }