Candyland 有一座糖果公园,公园里不仅有美丽的风景、好玩的游乐项目,还有许多免费糖果的发放点,这引来了许多贪吃的小朋友来糖果公园玩。
糖果公园的结构十分奇特,它由 nn 个游览点构成,每个游览点都有一个糖果发放处,我们可以依次将游览点编号为 11 至 nn。有 n−1n−1 条双向道路连接着这些游览点,并且整个糖果公园都是连通的,即从任何一个游览点出发都可以通过这些道路到达公园里的所有其它游览点。
糖果公园所发放的糖果种类非常丰富,总共 mm 种,它们的编号依次为 11 至 mm。每一个糖果发放处都只发放某种特定的糖果,我们用 cici 来表示 ii 号游览点的糖果。
来到公园里游玩的游客都不喜欢走回头路,他们总是从某个特定的游览点出发前往另一个特定的游览点,并游览途中的景点,这条路线一定是唯一的。他们经过每个游览点,都可以品尝到一颗对应种类的糖果。
大家对不同类型的糖果的喜爱程度都不尽相同。根据游客们的反馈打分,我们得到了糖果的美味指数,第 ii 种糖果的美味指数为 vivi。另外,如果一位游客反复地品尝同一种类的糖果,他肯定会觉得有一些腻。根据量化统计,我们得到了游客第 ii 次品尝某类糖果的新奇指数 wiwi,如果一位游客第 ii 次品尝第 jj 种糖果,那么他的愉悦指数 HH 将会增加对应的美味指数与新奇指数的乘积,即 vjwivjwi。这位游客游览公园的愉悦指数最终将是这些乘积的和。
当然,公园中每个糖果发放点所发放的糖果种类不一定是一成不变的。有时,一些糖果点所发放的糖果种类可能会更改(也只会是 mm 种中的一种),这样的目的是能够让游客们总是感受到惊喜。
糖果公园的工作人员小 A 接到了一个任务,那就是根据公园最近的数据统计出每位游客游玩公园的愉悦指数。但数学不好的小 A 一看到密密麻麻的数字就觉得头晕,作为小 A 最好的朋友,你决定帮他一把。
输入格式
第一行包含三个正整数 n,m,qn,m,q,分别表示游览点个数、糖果种类数和操作次数。
第二行包含 mm 个正整数 v1,v2,…,vmv1,v2,…,vm。
第三行包含 nn 个正整数 w1,w2,…,wnw1,w2,…,wn。
第四行到第 n+2n+2 行,每行包含两个正整数 ai,biai,bi,表示这两个游览点之间有路径可以直接到达。
第 n+3n+3 行包含 nn 个正整数 c1,c2,…,cnc1,c2,…,cn。
接下来 qq 行,每行包含三个整数 t,x,yt,x,y,表示一次操作:
若 tt 为 00,则 1≤x≤n1≤x≤n,1≤y≤m1≤y≤m,表示编号为 xx 的游览点发放的糖果类型改为 yy;
若 tt 为 11,则 1≤x,y≤n1≤x,y≤n,表示对出发点为 xx,终止点为 yy 的路线询问愉悦指数。
输出格式
按照输入的先后顺序,对于每个 tt 为 11 的操作输出一行,用一个正整数表示答案。
正解:树上带修改的莫队
首先dfs用类似BZOJ1086 王室联邦的方法对树进行分块,之后就是经典的树上带修改的莫队了。
与序列上的莫队不同的是,我们从上一次查询转移到下一次查询的方法。用类似亦或的思想(被同一个数亦或两次就相当于没有操作),于是我们可以对q[i-1].l到q[i].l以及q[i-1].r到q[i].r分别进行change操作,lca要特殊处理。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;
typedef long long LL;
const int maxn=200010;
int size;
vector<int>g[maxn];
int c[maxn],v[maxn],w[maxn];
int fa[maxn][20],dep[maxn];
struct Query{
int l,r,t,id;
int bx,by;
bool operator <(const Query& A)const{
return bx<A.bx || (bx==A.bx && (by<A.by || (by==A.by && t<A.t)));
}
}q[maxn];
struct node{
int x,y,last;
}r[maxn];
int last[maxn],vis[maxn];
int n,m,cntr,cntq,Q;
int be[maxn];
void read(int& x){
x=0; char c=getchar();
while(c<'0' || c>'9') c=getchar();
while(c>='0' && c<='9'){ x=x*10+c-'0'; c=getchar(); }
}
int top,cnt,tot;
int st[maxn],dfn[maxn];
void dfs(int x,int f){
dfn[x]=++tot;
int bottom=top;
for(int i=0;i<g[x].size();i++){
int v=g[x][i];
if(v==f) continue;
dep[v]=dep[x]+1;fa[v][0]=x;
dfs(v,x);
if(top-bottom>=size){
++cnt;
while(top!=bottom) be[st[top--]]=cnt;
}
}
st[++top]=x;
}
int lca(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
int d=dep[x]-dep[y];
for(int i=18;i>=0;i--) if(d & (1<<i)) x=fa[x][i];
if(x==y) return x;
for(int i=18;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int time[maxn];
LL ans,Ans[maxn];
void update(int x){
if(!vis[x]){
vis[x]=1;
ans+=(LL)v[c[x]]*w[++time[c[x]]];
}else{
vis[x]=0;
ans-=(LL)v[c[x]]*w[time[c[x]]--];
}
}
void modify(int x,int col){
if(!vis[x]) c[x]=col;
else{
update(x);
c[x]=col;
update(x);
}
}
void change(int x,int y){
while(x!=y){
if(dep[x]>dep[y]) update(x),x=fa[x][0];
else update(y),y=fa[y][0];
}
}
void solve(){
int head=q[1].t;
for(int i=1;i<=head;i++) modify(r[i].x,r[i].y);
change(q[1].l,q[1].r);
int LCA=lca(q[1].l,q[1].r);
update(LCA);
Ans[q[1].id]=ans;
update(LCA);
for(int i=2;i<=cntq;i++){
while(head<q[i].t) head++,modify(r[head].x,r[head].y);
while(head>q[i].t) modify(r[head].x,r[head].last),head--;
change(q[i-1].l,q[i].l);
change(q[i-1].r,q[i].r);
LCA=lca(q[i].l,q[i].r);
update(LCA);
Ans[q[i].id]=ans;
update(LCA);
}
}
int main(){
read(n);read(m);read(Q);
size=(int)pow(n,0.60);
for(int i=1;i<=m;i++) read(v[i]); for(int i=1;i<=n;i++) read(w[i]);
int u,v;
for(int i=1;i<n;i++){
read(u);read(v);
g[u].push_back(v);g[v].push_back(u);
}
for(int i=1;i<=n;i++) read(c[i]),last[i]=c[i];
dep[1]=1; dfs(1,0); for(int j=1;j<=18;j++) for(int i=1;i<=n;i++) fa[i][j]=fa[fa[i][j-1]][j-1];
while(top) be[st[top--]]=cnt;
while(Q--){
int op,x,y;
read(op);read(x);read(y);
if(!op){
r[++cntr].last=last[x],r[cntr].x=x,r[cntr].y=y,last[x]=y;
}else{
if(dfn[x]>dfn[y]) swap(x,y);
q[++cntq].l=x,q[cntq].r=y,q[cntq].t=cntr,q[cntq].id=cntq,q[cntq].bx=be[x],q[cntq].by=be[y];
}
}
sort(q+1,q+1+cntq);
solve();
for(int i=1;i<=cntq;i++) printf("%lld\n",Ans[i]);
return 0;
}
^_^