[WC2013] 糖果公園
題目描述
Candyland 有一座糖果公園,公園裡不僅有美麗的風景、好玩的遊樂專案,還有許多免費糖果的發放點,這引來了許多貪吃的小朋友來糖果公園遊玩。
糖果公園的結構十分奇特,它由 \(n\) 個遊覽點構成,每個遊覽點都有一個糖果發放處,我們可以依次將遊覽點編號為 \(1\) 至 \(n\)。有 \(n - 1\) 條雙向道路連線著這些遊覽點,並且整個糖果公園都是連通的,即從任何一個遊覽點出發都可以透過這些道路到達公園裡的所有其它遊覽點。
糖果公園所發放的糖果種類非常豐富,總共有 \(m\) 種,它們的編號依次為 \(1\) 至 \(m\)。每一個糖果發放處都只發放某種特定的糖果,我們用 \(C_i\) 來表示 \(i\) 號遊覽點的糖果。
來到公園裡遊玩的遊客都不喜歡走回頭路,他們總是從某個特定的遊覽點出發前往另一個特定的遊覽點,並遊覽途中的景點,這條路線一定是唯一的。他們經過每個遊覽點,都可以品嚐到一顆對應種類的糖果。
大家對不同型別糖果的喜愛程度都不盡相同。 根據遊客們的反饋打分,我們得到了糖果的美味指數, 第 \(i\) 種糖果的美味指數為 \(V_i\)。另外,如果一位遊客反覆地品嚐同一種類的糖果,他肯定會覺得有一些膩。根據量化統計,我們得到了遊客第 \(i\) 次品嚐某類糖果的新奇指數 \(W_i\)。如果一位遊客第 \(i\) 次品嚐第 \(j\) 種糖果,那麼他的愉悅指數 \(H\) 將會增加對應的美味指數與新奇指數的乘積,即 \(V_j \times W_i\)。這位遊客遊覽公園的愉悅指數最終將是這些乘積的和。
當然,公園中每個糖果發放點所發放的糖果種類不一定是一成不變的。有時,一些糖果點所發放的糖果種類可能會更改(也只會是 \(m\) 種中的一種),這樣的目的是能夠讓遊客們總是感受到驚喜。
糖果公園的工作人員小 A 接到了一個任務,那就是根據公園最近的資料統計出每位遊客遊玩公園的愉悅指數。但數學不好的小 A 一看到密密麻麻的數字就覺得頭暈,作為小 A 最好的朋友,你決定幫他一把。
輸入格式
從檔案 park.in
中讀入資料。
第一行包含三個正整數 \(n, m, q\), 分別表示遊覽點個數、 糖果種類數和操作次數。
第二行包含 \(m\) 個正整數 \(V_1, V_2, \ldots, V_m\)。
第三行包含 \(n\) 個正整數 \(W_1, W_2, \ldots, W_n\)。
第四行到第 \(n + 2\) 行,每行包含兩個正整數 \(A_i, B_i\),表示這兩個遊覽點之間有路徑可以直接到達。
第 \(n + 3\) 行包含 \(n\) 個正整數 \(C_1, C_2, \ldots, C_n\)。
接下來 \(q\) 行, 每行包含三個整數 \(Type, x, y\),表示一次操作:
- 若 \(Type\) 為 \(0\),則 \(1 \leq x \leq n\), \(1 \leq y \leq m\),表示將編號為 \(x\) 的遊覽點發放的糖果型別改為 \(y\);
- 若 \(Type\) 為 \(1\),則 \(1 \leq x, y \leq n\),表示對出發點為 \(x\),終止點為 \(y\) 的路線詢問愉悅指數。
輸出格式
輸出到檔案 park.out
中。
按照輸入的先後順序,對於每個 \(Type\) 為 \(1\) 的操作輸出一行,用一個正整數表示答案。
樣例 #1
樣例輸入 #1
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
樣例輸出 #1
84
131
27
84
提示
【樣例解釋】
我們分別用
代表 \(C_i\) 為 \(1\)、 \(2\)、 \(3\) 的節點,在修改之前:
在將 \(C_2\) 修改為 \(1\) 之後:
【資料規模與約定】
對於所有的資料: \(1 \leq V_i, W_i \leq 10^6\),\(1 \leq A_i, B_i \leq n\), \(1 \leq C_i \leq m\), \(W_1, W_2, \ldots, W_n\) 是非遞增序列,即對任意 \(1 < i \leq n\), 滿足 \(W_i \le W_{i-1}\)。
其它的限制條件如下表所示:
普通帶修莫隊,不太卡常數,TLE多半程式問題
時間複雜度 約 O(n^1.3)
#include<bits/stdc++.h>
using namespace std;
typedef __int128 i128;
typedef long long ll;
//#define int long long
int read() {
int res=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c))
res=(res<<1)+(res<<3)+(c^48),c=getchar();
return res;
}
const int N=2e5+7;
int n,m,q,len;
int cq,cp;//詢問次數 修改次數
int a[N];
int v[N],w[N],col[N];
ll res,ans[N];
struct Q{
int id,l,r,x,y,t;
inline bool operator<(const Q b)const{
if(x^b.x)return x<b.x;
if(y^b.y)return x&1?y<b.y:y>b.y;
return t<b.t;
}
}q1[N];
struct C{
int x,y;
}q2[N];
int cnt[N],vis[N];
int head[N],ne[N],to[N],idx;
int fa[N],top[N],dep[N],siz[N],son[N],pos[N],dfn;
inline void add(int u,int v){
to[++idx]=v;
ne[idx]=head[u];
head[u]=idx;
}
/*-----------------------圖部分--------------------------*/
void dfs1(int u){
siz[u]=1;
dep[u]=dep[fa[u]]+1;
for(int i=head[u];i;i=ne[i]){
int v=to[i];
if(v==fa[u]) continue ;
fa[v]=u;
dfs1(v);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]]) son[u]=v;
}
}
void dfs2(int u,int p){
top[u]=p;
a[++dfn]=u;
pos[u]=dfn;//樹上節點在序列中的下標
if(son[u]) dfs2(son[u],p);
for(int i=head[u];i;i=ne[i]){
int v=to[i];
if(v!=fa[u]&&v!=son[u]) dfs2(v,v);
}
a[++dfn]=u;//尤拉序(不同於dfn序,每個點在序列中出現兩次,中間的節點就是子樹)
}
inline int lca(int u,int v){//lca
for(;top[u]^top[v];dep[top[u]]>dep[top[v]]?u=fa[top[u]]:v=fa[top[v]]);
return dep[u]<dep[v]?u:v;
}
/*-----------------------莫隊部分--------------------------*/
inline void sol(int x){
vis[x]^=1;
if(vis[x]) res+=1ll*w[++cnt[col[x]]]*v[col[x]];
else res-=1ll*w[cnt[col[x]]--]*v[col[x]];
}
inline void upd(int id){
int u=q2[id].x,x=q2[id].y,y=col[u];
if(vis[u]) res+=(ll)w[++cnt[x]]*v[x]-(ll) w[cnt[y]--]*v[y];//更改帶來的貢獻
q2[id].y=y,col[u]=x;//swap操作
}
/*-----------------------主函式--------------------------*/
signed main() {
n=read(),m=read(),q=read();
for(int i=1;i<=m;++i) v[i]=read();
for(int i=1;i<=n;++i) w[i]=read();
for(int i=2;i<=n;++i){
int u=read(),v=read();
add(u,v),add(v,u);
}
for(int i=1;i<=n;++i) col[i]=read();
dfs1(1),dfs2(1,1);
int x,y,t;
for(int i=1;i<=q;++i){
x=read();
if(x){
x=read(),y=read();
if(pos[x]>pos[y]) swap(x,y);
q1[++cq]=(Q){cq,x,y,pos[x],pos[y],cp};
}
else{
x=read(),y=read();
q2[++cp]=(C){x,y};
}
}
len=pow(n,0.67);
for(int i=1;i<=cq;++i)
q1[i].x/=len,q1[i].y/=len;
sort(q1+1,q1+1+cq);
int lcur=pos[q1[1].l],rcur=pos[q1[1].l]-1,tcur=0;
for(int i=1;i<=cq;++i){
x=pos[q1[i].l],y=pos[q1[i].r],t=q1[i].t;
while(lcur>x) sol(a[--lcur]);
while(lcur<x) sol(a[lcur++]);
while(rcur<y) sol(a[++rcur]);
while(rcur>y) sol(a[rcur--]);
while(tcur>t) upd(tcur--);
while(tcur<t) upd(++tcur);
int u=q1[i].l,v=q1[i].r;
int p=lca(u,v);
if(u!=p){//如果起點不是lca,貢獻不會被計算的
//例如序列12443321 詢問4->3 也就是[3,5] 2節點不會被訪問
sol(u);
if(v!=p) sol(p);
}
ans[q1[i].id]=res;
if(u!=p){
sol(u);
if(v!=p) sol(p);
}
}
for(int i=1;i<=cq;++i)
printf("%lld\n",ans[i]);
return 0;
}