【树上带修莫队】糖果公园

传送门:UOJ58

WC2013


思路:

  • 树上带修莫队的做法比较显然;
  • 先把树转换成括号序列(树分块我可能不会);
  • 然后用带修莫队;
  • 没了。。。

注意:

  • 此题long long
  • 要保留当前修改前的糖果,因为排完序后不是按时间升序

代码如下:

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
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define LL long long
#define N 300000
#define BASE 1800
#define block(x) (x/BASE)
LL n,m,p,op,x,y,clk,tot,tot2,tmp,cnt;
LL a[N],b[N],c[N],last[N],head[N],fa[N],deep[N],size[N],l[N],r[N],pos[N],top[N],sum[N],ans[N];
bool vis[N];
struct edge{LL v,nxt;}e[N];
struct query{LL i,j,k,lca,ID;}qry[N];
struct modify{LL x,y,z;}mdy[N];
LL getint(){
char ch; LL ss=0;
for (ch=getchar();ch<'0' || ch>'9';ch=getchar());
for (;ch>='0' && ch<='9';ch=getchar()) ss=ss*10+ch-'0';
return ss;
}
bool cmp(query x,query y){//block(i)为第一关键字,block(j)为第二关键字,k为第三关键字
if (block(x.i)!=block(y.i)) return block(x.i)<block(y.i);
if (block(x.j)!=block(y.j)) return block(x.j)<block(y.j);
return x.k<y.k;
}
void add(LL x,LL y){e[++tot].v=y; e[tot].nxt=head[x]; head[x]=tot;}
void dfs1(LL cur){
if (!fa[cur]) fa[cur]=cur; deep[cur]=deep[fa[cur]]+1; size[cur]=1;
l[cur]=++tot2; pos[tot2]=cur;
for (LL i=head[cur],v;i;i=e[i].nxt)
if ((v=e[i].v)!=fa[cur]){fa[v]=cur; dfs1(v); size[cur]+=size[v];}
r[cur]=++tot2; pos[tot2]=cur;
}
void dfs2(LL cur){
if (!top[cur]) top[cur]=cur;
LL t=0; for (LL i=head[cur],v;i;i=e[i].nxt) if ((v=e[i].v)!=fa[cur] && size[v]>size[t]) t=v;
if (!t) return; top[t]=top[cur]; dfs2(t);
for (LL i=head[cur];i;i=e[i].nxt) if (e[i].v!=fa[cur] && e[i].v!=t) dfs2(e[i].v);
}
LL lca(LL u,LL v){
for (;top[u]!=top[v];u=fa[top[u]]) if (deep[top[u]]<deep[top[v]]) swap(u,v);
return deep[u]<deep[v]?u:v;
}
void inc(LL x){
if (!vis[x]) tmp+=a[c[x]]*b[++sum[c[x]]]; else tmp-=a[c[x]]*b[sum[c[x]]--];
vis[x]^=1;
}
void change(LL x,LL y){if (vis[x]){inc(x); c[x]=y; inc(x);} else c[x]=y;}
//vis[x]说明如果当前区间内括号无法抵消(点x在路径上),就改变访问次数,否则只改变值
int main(){
n=getint(); m=getint(); p=getint();
for (LL i=1;i<=m;i++) a[i]=getint();
for (LL i=1;i<=n;i++) b[i]=getint();
for (LL i=1;i<n;i++){x=getint(); y=getint(); add(x,y); add(y,x);}
for (LL i=1;i<=n;i++){c[i]=getint(); last[i]=c[i];}
dfs1(1); dfs2(1);
for (LL i=1;i<=p;i++){
op=getint(); x=getint(); y=getint();
if (op==0){mdy[++clk]=(modify){x,last[x],y}; last[x]=y;}
if (op==1){
if (l[x]>l[y]) swap(x,y);
LL LCA=lca(x,y);
if (x==LCA)qry[++cnt]=(query){l[x],l[y],clk,0,cnt};
else qry[++cnt]=(query){r[x],l[y],clk,LCA,cnt};
}
}
sort(qry+1,qry+cnt+1,cmp);
for (LL i=0,j=0,k=0,xb=1;xb<=cnt;xb++){
while (k<qry[xb].k){k++; change(mdy[k].x,mdy[k].z);}
while (k>qry[xb].k){change(mdy[k].x,mdy[k].y); k--;}
while (i>qry[xb].i) inc(pos[--i]);
while (j<qry[xb].j) inc(pos[++j]);
while (i<qry[xb].i) inc(pos[i++]);
while (j>qry[xb].j) inc(pos[j--]);
if (qry[xb].lca) inc(qry[xb].lca);
ans[qry[xb].ID]=tmp;
if (qry[xb].lca) inc(qry[xb].lca);
}
for (LL i=1;i<=cnt;i++) printf("%lld\n",ans[i]);
return 0;
}