题目描述
有一座糖果公园,公园里不仅有美丽的风景、好玩的游乐项目,还有许多免费糖果的发放点,这引来了许多贪吃的小朋友来糖果公园玩。
糖果公园的结构十分奇特,它由 个游览点构成,每个游览点都有一个糖果发放处,我们可以依次将游览点编号为 至 。有 条双向道路连接着这些游览点,并且整个糖果公园都是连通的,即从任何一个游览点出发都可以通过这些道路到达公园里的所有其它游览点。
糖果公园所发放的糖果种类非常丰富,总共 种,它们的编号依次为 至 。每一个糖果发放处都只发放某种特定的糖果,我们用 来表示 号游览点的糖果。
来到公园里游玩的游客都不喜欢走回头路,他们总是从某个特定的游览点出发前往另一个特定的游览点,并游览途中的景点,这条路线一定是唯一的。他们经过每个游览点,都可以品尝到一颗对应种类的糖果。
大家对不同类型的糖果的喜爱程度都不尽相同。根据游客们的反馈打分,我们得到了糖果的美味指数,第 种糖果的美味指数为 。另外,如果一位游客反复地品尝同一种类的糖果,他肯定会觉得有一些腻。根据量化统计,我们得到了游客第 次品尝某类糖果的新奇指数 ,如果一位游客第 次品尝第 种糖果,那么他的愉悦指数 将会增加对应的美味指数与新奇指数的乘积,即 。这位游客游览公园的愉悦指数最终将是这些乘积的和。
当然,公园中每个糖果发放点所发放的糖果种类不一定是一成不变的。有时,一些糖果点所发放的糖果种类可能会更改(也只会是 种中的一种),这样的目的是能够让游客们总是感受到惊喜。
糖果公园的工作人员小 接到了一个任务,那就是根据公园最近的数据统计出每位游客游玩公园的愉悦指数。但数学不好的小 一看到密密麻麻的数字就觉得头晕,作为小 最好的朋友,你决定帮他一把。
输入格式
第一行包含三个正整数 ,分别表示游览点个数、糖果种类数和操作次数。
第二行包含 个正整数 。
第三行包含 个正整数 。
第四行到第 行,每行包含两个正整数 ,表示这两个游览点之间有路径可以直接到达。
第 行包含 个正整数
接下来 行,每行包含三个整数 ,表示一次操作:
若 为 ,则 ,表示编号为 的游览点发放的糖果类型改为 ;
若 为 ,则 ,表示对出发点为 ,终止点为 的路线询问愉悦指数。
输出格式
按照输入的先后顺序,对于每个 为 的操作输出一行,用一个正整数表示答案。
样例一
Input
4 3 5
1 9 2
7 6 5 1
2 3
3 1
3 4
1 2 3 2
1 1 2
1 4 2
0 2 1
1 1 2
1 4 2
Output
84
131
27
84
Hint
分析:
带修改的树上莫队
既然我们知道了树上莫队怎么搞
也知道了带修改的序列莫队怎么破
那么我们直接把这两个结合起来即可
我们还是处理出 序
记录每个询问之前的修改次数
每次处理询问之前暴力移动修改标记
如果我们要修改结点
,而当前区间中的
说明
可以影响当前询问,我们就需要修改一下
我一开始看见题目描述中有起点和终点,吓了一跳,还需要规定行走方向啊
后来仔细一想,不管行走方向是什么样,我们最后统计出来的贡献肯定都是一样的
tip
分块大小为 最快
询问中的左右端点是 序上的,但是修改的时候都是结点编号,不要搞错了
对于贡献的计算,想清楚就好
注意空间(
序的长度为
,空间开小RE了一发)
最后的答案要开ll
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=200010;
int n,m,Q,cnt_q=0,cnt_c=0;
int v[N],w[N],C[N],cnt[N],vis[N];
ll tmp,ans[N];
struct node{
int x,y,p,block,num,id;
}q[N];
struct point{
int x,y;
}c[N];
struct E{
int y,nxt;
}way[N<<1];
int st[N],tot=0,dfn[N],clo=0,in[N],out[N],pre[N][20],deep[N];
void add(int u,int w) {
tot++;way[tot].y=w;way[tot].nxt=st[u];st[u]=tot;
tot++;way[tot].y=u;way[tot].nxt=st[w];st[w]=tot;
}
int cmp(const node &a,const node &b) {
if (a.block!=b.block) return a.block<b.block;
else if (a.y!=b.y) return a.y<b.y;
else return a.num<b.num;
}
void dfs(int now,int fa,int dep) {
pre[now][0]=fa;
deep[now]=dep;
for (int i=1;i<20;i++) {
if ((1<<i)>dep) break;
pre[now][i]=pre[pre[now][i-1]][i-1];
}
dfn[++clo]=now;
in[now]=clo;
for (int i=st[now];i;i=way[i].nxt)
if (way[i].y!=fa)
dfs(way[i].y,now,dep+1);
dfn[++clo]=now;
out[now]=clo;
}
int LCA(int x,int y) {
if (deep[x]<deep[y]) swap(x,y);
int d=deep[x]-deep[y];
if (d)
for (int i=0;i<20&&d;i++,d>>=1)
if (d&1)
x=pre[x][i];
if (x==y) return x;
for (int i=19;i>=0;i--)
if (pre[x][i]!=pre[y][i]) {
x=pre[x][i];
y=pre[y][i];
}
return pre[x][0];
}
void update(int x) {
int co=C[x];
if (vis[x]) { //删除影响
tmp-=(ll)w[cnt[co]]*v[co];
cnt[co]--;
}
else {
cnt[co]++;
tmp+=(ll)w[cnt[co]]*v[co];
}
vis[x]^=1;
}
void change(int x,int l,int r) {
bool flag=0;
if (vis[c[x].x]) update(c[x].x),flag=1; //这个结点在序列中
swap(C[c[x].x],c[x].y);
if (flag) update(c[x].x);
}
void solve() {
int l=1,r=0,now=0;
tmp=0;
for (int i=1;i<=cnt_q;i++) {
while (q[i].y<r) update(dfn[r]),r--;
while (q[i].y>r) r++,update(dfn[r]);
while (q[i].x<l) l--,update(dfn[l]);
while (q[i].x>l) update(dfn[l]),l++;
while (now<q[i].num) now++,change(now,l,r);
while (now>q[i].num) change(now,l,r),now--;
if (q[i].p) update(q[i].p); //lca
ans[q[i].id]=tmp;
if (q[i].p) update(q[i].p);
}
}
int main()
{
scanf("%d%d%d",&n,&m,&Q);
for (int i=1;i<=m;i++) scanf("%d",&v[i]);
for (int i=1;i<=n;i++) scanf("%d",&w[i]);
for (int i=1;i<n;i++) {
int u,w;
scanf("%d%d",&u,&w);
add(u,w);
}
dfs(1,0,1);
for (int i=1;i<=n;i++) scanf("%d",&C[i]);
int unit=(int)pow(n,2.0/3);
int opt,x,y;
for (int i=1;i<=Q;i++) {
scanf("%d%d%d",&opt,&x,&y);
if (opt==0) {
cnt_c++;
c[cnt_c].x=x; c[cnt_c].y=y;
}
else {
cnt_q++;
if (in[x]>in[y]) swap(x,y);
int p=LCA(x,y);
if (p==x||p==y) {
q[cnt_q].x=in[x];
q[cnt_q].y=in[y];
q[cnt_q].p=0;
q[cnt_q].block=(q[cnt_q].x-1)/unit+1;
q[cnt_q].num=cnt_c;
q[cnt_q].id=cnt_q;
}
else {
q[cnt_q].x=out[x];
q[cnt_q].y=in[y];
q[cnt_q].p=p;
q[cnt_q].block=(q[cnt_q].x-1)/unit+1;
q[cnt_q].num=cnt_c;
q[cnt_q].id=cnt_q;
}
}
}
sort(q+1,q+1+cnt_q,cmp);
solve();
for (int i=1;i<=cnt_q;i++) printf("%lld\n",ans[i]);
return 0;
}