Luogu 4074 [WC2013 糖果公园]

传送门

正确性什么的先咕咕咕一下吧,我也不太会。


题目大意

有一个$n\le10^5$个节点的树,每个节点有一个颜色。

每次询问:
$$
\sum_cval_c\times\sum_{i=1}^{cnt}w_i
$$
$val_c$表示该颜色的价值

$cnt$表示颜色出现的次数

$w_i$表示该颜色出现$i$次后的价值

带修改。

解析

可以考虑用$dfs$把这棵树拍平。

我们记录到达每个点的$dfn$和离开每个点的$dfn$:

给出一个例子

在上图的例子中,我们设蓝色数字为$st_i$表示到达的$dfn$,绿色数字为$ed_i$表示离开的$dfn$。

对于一条链$(x,y)$,我们分类讨论(以下假设$st_x<st_y$):

  1. 若$x$和$y$构成祖孙关系,考虑区间$[st_x,st_y]$。
  2. 否则考虑区间$[ed_x,st_y]$。

我们发现,所有不在链上的点所对应的$dfn$(包括$st$和$ed$)都经过了正好偶数遍!

⬆这个是假的,我们要特判$lca$。

然后就可以随便莫队了。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#define DREP(i, s, e) for(int i = s; i >= e ;i--)
#define REP(i, s, e) for(int i = s; i <= e ;i++)

#define DEBUG fprintf(stderr, "Passing [%s] in Line %d\n", __FUNCTION__, __LINE__)
#define chkmax(a, b) a = max(a, b)
#define chkmin(a, b) a = min(a, b)

#include <algorithm>
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn = 100000 + 10, maxm = 100000 + 10, maxq = 100000 + 10, block_siz = 800;

int bg[maxn], ne[maxm << 1], to[maxm << 1], e;
inline void add(int x, int y)
{
e++;
to[e] = y;
ne[e] = bg[x];
bg[x] = e;
}

template <typename T> inline T read()
{
T ans(0), p(1);
char c = getchar();
while (!isdigit(c))
{
if (c == '-') p = -1;
c = getchar();
}
while (isdigit(c))
{
ans = ans * 10 + c - 48;
c = getchar();
}
return ans * p;
}

int n, m, q, c[maxn];

int st[maxn], ed[maxn], back[maxn << 1], dfs_clock, top[maxn], fa[maxn], siz[maxn], hvy[maxn], depth[maxn];
void dfs1(int x)
{
back[st[x] = ++dfs_clock] = x;
siz[x] = 1;
for (int i = bg[x]; i ; i = ne[i])
if (to[i] ^ fa[x])
{
fa[to[i]] = x;
depth[to[i]] = depth[x] + 1;
dfs1(to[i]);
siz[x] += siz[to[i]];
if (siz[to[i]] > siz[hvy[x]]) hvy[x] = to[i];
}
back[ed[x] = ++dfs_clock] = x;
}
void dfs2(int x, int y)
{
top[x] = y;
if (hvy[x]) dfs2(hvy[x], y);
for (int i = bg[x]; i ; i = ne[i])
if (to[i] ^ fa[x] && to[i] ^ hvy[x]) dfs2(to[i], to[i]);
}
inline int lca(int x, int y)
{
while (top[x] ^ top[y])
{
if (depth[top[x]] < depth[top[y]]) swap(x, y);
x = fa[top[x]];
}
return depth[x] < depth[y] ? x : y;
}
int belong[maxn << 1];
struct query
{
int l, r, t, spj, id;
query(){}
query(int _l, int _r, int _t, int _id, int _spj = 0) : l(_l), r(_r), t(_t), id(_id), spj(_spj) {}

}qlist[maxq];
inline bool operator < (query A, query B) {return belong[A.l] < belong[B.l] || (belong[A.l] == belong[A.l] && belong[A.r] < belong[B.r]) || (belong[A.l] == belong[B.l] && belong[A.r] == belong[B.r] && A.t < B.t);}
int cur, Q;
pair <int, int> cz[maxq];

long long ans, cnt[maxm], v[maxn], w[maxn];
bool vis[maxn];
int l, r, T;
inline void timego()
{
T++;
int x = cz[T].first, &b = cz[T].second, &a = c[x];

if (!vis[x]) swap(a, b);
else
{
ans -= w[(cnt[a]--)] * v[a];
swap(a, b);
ans += w[(++cnt[a])] * v[a];
}
}
inline void timeback()
{
int x = cz[T].first, &b = cz[T].second, &a = c[x];

if (!vis[x]) swap(a, b);
else
{
ans -= w[(cnt[a]--)] * v[a];
swap(a, b);
ans += w[(++cnt[a])] * v[a];
}
T--;
}
inline void add(int x)
{
if (!vis[x]) ans += w[(++cnt[c[x]])] * v[c[x]];
else ans -= w[(cnt[c[x]]--)] * v[c[x]];
vis[x] ^= 1;
}
inline void del(int x)
{
if (vis[x]) ans -= w[(cnt[c[x]]--)] * v[c[x]];
else ans += w[(++cnt[c[x]])] * v[c[x]];
vis[x] ^= 1;
}
bool needoutput[maxq];
long long realans[maxq];

signed main()
{
#ifdef CraZYali
freopen("4074.in", "r", stdin);
freopen("4074.out", "w", stdout);
#endif
cin >> n >> m >> q;
REP(i, 1, m) v[i] = read<int>();
REP(i, 1, n) w[i] = read<int>();
REP(i, 2, n)
{
int x(read<int>()), y(read<int>());
add(x, y);add(y, x);
}
REP(i, 1, n) c[i] = read<int>();
dfs1(1);
dfs2(1, 1);
REP(i, 1, q)
{
int opt(read<int>()), x(read<int>()), y(read<int>());
if (opt == 0) cz[++cur] = make_pair(x, y);
else
{
needoutput[i] = 1;
if (st[x] > st[y]) swap(x, y);
int l(lca(x, y));
if (x ^ l && y ^ l) qlist[++Q] = query(ed[x], st[y], cur, i, l);
else qlist[++Q] = query(st[x], st[y], cur, i);
}
}
REP(i, 1, n + n) belong[i] = i / block_siz;
sort(qlist + 1, qlist + 1 + Q);
REP(i, 1, Q)
{
while (T < qlist[i].t) timego();
while (T > qlist[i].t) timeback();
while (l < qlist[i].l) del(back[l++]);
while (l > qlist[i].l) add(back[--l]);
while (r < qlist[i].r) add(back[++r]);
while (r > qlist[i].r) del(back[r--]);
if (qlist[i].spj) add(qlist[i].spj);
realans[qlist[i].id] = ans;
if (qlist[i].spj) del(qlist[i].spj);
}
REP(i, 1, q) if (needoutput[i]) printf("%lld\n", realans[i]);
return 0;
}

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×