题目传送门: http://www.lydsy.com/JudgeOnline/problem.php?id=3052
最近不太想写博客了,退役前还是写点学的东西吧。
这个坑从我会莫队开始就挖在这里了。。
莫队: http://blog.csdn.net/nickwzk/article/details/52954097
可是在树上怎么莫队呢?
这有两种非常妙的方法,dfs序和王室联邦分块(bzoj1086)
我看网上大家都用dfs序的版本,就作死写了一个王室联邦分块的,然后常数巨大(大概不怪王室联邦分块怪我常数大
然后这个是一个树上的带修改的莫队,所以当块大小等于
n23
的时候复杂度最优。
借用王室联邦分块,另块大小为B=
n23
,块的个数为p。每个节点i属于belong[i]。我们可以发现(belong[u], belong[v])最多
p2
个取值。
因为已经按块排好序了,所以我们可以将情况分为两种。一种是当前移动后u,v都在原块内,那么移动总复杂度为O(
B∗q
);另一种是u,v不在原来的块内,那么这种情况最多出现
p2次
,每次移动最坏情况为O(
n+n+q
)。又因为n与q同阶,所以可以得到总的时间复杂度为O(
n53
)
然后在树上移动的时候,记录一个vis数组,到达一个点的时候将其取反,如果已经存在当前统计中就将它删去,若没有就将它加入。对于时间的修改可以放在移动之前也可以放在移动之后,必须要使这个操作可逆,所以修改之后要记录下修改之前的糖果种类。
对于lca(u,v),要在记录答案之前加上,在记录答案之后删去。
自带巨大常数的Code:
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
const int MAXN = 100003;
const int S = 17;
struct Edge {
int to, nxt;
Edge() {}
Edge(int _to, int _nxt):to(_to), nxt(_nxt) {}
}e[MAXN << 1];
int h[MAXN], p;
int stack[MAXN], top, belong[MAXN], cnt, sz;
struct Node {
int l, r, id, ti;
bool operator < (const Node &x) const {
return belong[l] < belong[x.l] || (belong[l] == belong[x.l] && belong[r] < belong[x.r]) || (belong[l] == belong[x.l] && belong[r] == belong[x.r] && ti < x.ti);
}
}q[MAXN];
struct Node2 {
int l, r, ti;
}qq[MAXN];
int n, m, Q, Q0, Q1;
int V[MAXN], W[MAXN], C[MAXN];
int fa[MAXN][S + 3], dep[MAXN];
long long ans[MAXN], tans;
int vis[MAXN], cur[MAXN];
long long sum[MAXN];
int l, r, tm;
inline int read() {
int x = 0; char ch = getchar(); bool fg = 0;
while(ch < '0' || ch > '9') { if(ch == '-') fg = 1; ch = getchar(); }
while(ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return fg ? -x : x;
}
inline void add_edge(int u, int v) {
e[++p] = Edge(v, h[u]); h[u] = p;
e[++p] = Edge(u, h[v]); h[v] = p;
}
void dfs(int u, int f) {
fa[u][0] = f;
dep[u] = dep[f] + 1;
int bot = top;
for(int i = h[u]; i; i = e[i].nxt) {
int v = e[i].to;
if(v == f) continue;
dfs(v, u);
if(top - bot >= sz) {
cnt++;
while(top != bot) belong[stack[top--]] = cnt;
}
}
stack[++top] = u;
}
void G(int &u, int step) {
for(int i = 0; i < S; i++) if((1 << i) & step) u = fa[u][i];
}
int lca(int u, int v) {
if(dep[u] > dep[v]) swap(u, v);
G(v, dep[v] - dep[u]);
if(u == v) return u;
for(int i = S; i >= 0; i--) if(fa[u][i] != fa[v][i]) {
u = fa[u][i]; v = fa[v][i];
}
return fa[u][0];
}
inline void modify(int u) {
tans -= V[C[u]] * sum[cur[C[u]]];
cur[C[u]] += vis[u];
vis[u] = -vis[u];
tans += V[C[u]] * sum[cur[C[u]]];
}
inline void update(int u, int v) {
if(u == v) return;
if(dep[u] > dep[v]) swap(u, v);
while(dep[v] > dep[u]) {
modify(v);
v = fa[v][0];
}
while(u != v) {
modify(u); modify(v);
u = fa[u][0]; v = fa[v][0];
}
}
inline void upd(int t) {
if(vis[qq[t].l] == -1) {
modify(qq[t].l);
swap(C[qq[t].l], qq[t].r);
modify(qq[t].l);
}
else swap(C[qq[t].l], qq[t].r);
}
inline void moveto(int u, int v) {
update(l, u); update(r, v);
l = u; r = v;
}
int main() {
n = read(); m = read(); Q = read();
sz = (int)pow(n, 2.0 / 3.0);
for(int i = 1; i <= m; i++) V[i] = read();
for(int i = 1; i <= n; i++) W[i] = read();
for(int i = 1, u, v; i < n; i++) {
u = read(); v = read();
add_edge(u, v);
}
for(int i = 1; i <= n; i++) {
C[i] = read();
vis[i] = 1;
sum[i] = sum[i - 1] + W[i];
}
for(int i = 1, tp; i <= Q; i++) {
tp = read();
if(tp) {
++Q1;
q[Q1].l = read(); q[Q1].r = read();
q[Q1].id = Q1;
q[Q1].ti = i;
}
else {
++Q0;
qq[Q0].l = read(); qq[Q0].r = read();
qq[Q0].ti = i;
}
}
dfs(1, 0);
while(top) belong[stack[top--]] = cnt;
sort(q + 1, q + Q1 + 1);
for(int k = 1; k <= S; k++) {
for(int i = 1; i <= n; i++) {
fa[i][k] = fa[fa[i][k - 1]][k - 1];
}
}
for(int i = 1; i <= Q1; i++) {
if(belong[q[i].l] > belong[q[i].r]) swap(q[i].l, q[i].r);
moveto(q[i].l, q[i].r);
int lc = lca(l, r);
modify(lc);
while(qq[tm + 1].ti < q[i].ti && tm < Q0) upd(++tm);
while(qq[tm].ti > q[i].ti) upd(tm--);
ans[q[i].id] = tans;
modify(lc);
}
for(int i = 1; i <= Q1; i++) printf("%lld\n", ans[i]);
return 0;
}