problem
小 A 所在的地区有 n n n 个点,标号为 1 ∼ n 1 ∼ n 1∼n。每个节点都连出去恰好一条有向边,设 i i i 连出去的点是 A i A_i Ai。保证 A i ≠ i A_i \ne i Ai=i 且 A A i ≠ i A_{A_i} \ne i AAi=i。
每个点上有一些糖果,第 i i i 个节点上的糖果数量为 B i B_i Bi,小 A 定义一个节点的稠密度为 C i C_i Ci, C i C_i Ci 求法如下:
假设和 i i i 距离不超过 1 1 1 的点有 D i D_i Di 个(包括 i i i 连出去的点、连向 i i i 的点以及 i i i 自己),分别是 P 1 , P 2 , . . . P D i P_1, P_2, ...P_{D_i} P1,P2,...PDi。
设 E i = ⌊ B i D i ⌋ E_i = ⌊\frac {B_i}{D_i}⌋ Ei=⌊DiBi⌋,那么 C i = B i − D i × E i + ∑ j = 1 D i E P j C_i = B_i-D_i\times E_i+\sum_{j=1}^{D_i}E_{P_j} Ci=Bi−Di×Ei+∑j=1DiEPj。
现在小 A 想让你实现一个糖果稠密度分析仪,要支持三种操作:
- 1 1 1 i i i j j j:表示把 i i i 的出边改为 j j j,即令 A i = j Ai = j Ai=j,保证 j ≠ i j \ne i j=i 且 A j ≠ i A_j \ne i Aj=i。
- 2 2 2 i i i:表示询问 i i i 点的稠密度,即你需要输出 C i C_i Ci。
- 3 3 3:询问所有节点中, C i C_i Ci 的最小值和最大值。
数据范围: 3 ≤ n ≤ 1 0 5 3 ≤ n ≤ 10^5 3≤n≤105, 1 ≤ q ≤ 1 0 5 1 ≤ q ≤ 10^5 1≤q≤105, 1 ≤ B i ≤ 1 0 12 1 ≤ B_i ≤ 10^{12} 1≤Bi≤1012, 1 ≤ A i ≤ n 1 ≤ A_i ≤ n 1≤Ai≤n。
solution
感觉这道题想通了就不算很难了,主要是代码细节问题。
把 C i C_i Ci 中 A i A_i Ai 的贡献与其他点的贡献分开,即我们先不考虑 A i A_i Ai,最后加上 E A i E_{A_i} EAi。
记集合 s o n i = { C j ∣ A j = i } son_i=\{C_j \mid A_j=i\} soni={Cj∣Aj=i}(注意这里的 C j C_j Cj 也是不考虑 E A j E_{A_j} EAj 的)。对每个点都维护一个这样的集合,然后把 E i + m a x { s o n i } E_i+max\{son_i\} Ei+max{soni} 和 E i + m i n { s o n i } E_i+min\{son_i\} Ei+min{soni} 丢到全局的集合,最后全局集合的最大最小值就是 3 3 3 的答案。
对于 2 2 2,答案也就是 C x + E A x C_x+E_{A_x} Cx+EAx。
那么对于 1 1 1 的修改,考虑会有这些东西的更改:
- i i i 点的 A A A 值。
- 原 A i A_i Ai 点的 C , D , E C,D,E C,D,E 值和 s o n son son 集合。
- A A i A_{A_i} AAi 点的 C C C 值和 s o n son son 集合。
- A A A i A_{A_{A_i}} AAAi 点的 s o n son son 集合。
- j j j 点的 C , D , E C,D,E C,D,E 值和 s o n son son 集合。
- A j A_j Aj 的 C C C 值和 s o n son son 集合。
- A A j A_{A_j} AAj 的 s o n son son 集合。
这些其实在草稿纸上好好推一推就可以得到,具体怎么改根据公式也很容易推出。
时间复杂度 O ( n log n ) O(n\log n) O(nlogn)。
code
#include<bits/stdc++.h>
#define N 100005
#define ll long long
using namespace std;
int n,q;
ll A[N],B[N],C[N],D[N],E[N];
multiset<ll>son[N],Ans;
void Insert(int x){
if(!son[x].empty()){
Ans.insert(E[x]+*son[x].begin());
Ans.insert(E[x]+*son[x].rbegin());
}
}
void Delete(int x){
if(!son[x].empty()){
Ans.erase(Ans.find(E[x]+*son[x].begin()));
Ans.erase(Ans.find(E[x]+*son[x].rbegin()));
}
}
void ins(int fa,int x) {son[fa].insert(C[x]);}
void del(int fa,int x) {son[fa].erase(son[fa].find(C[x]));}
void Modify(int i,int j){
int x=A[i],y=A[x],z=A[y];
Delete(x),Delete(y),Delete(z);del(z,y),del(y,x),del(x,i);
C[y]-=E[x],C[x]-=E[i]-E[x]*(D[x]-1);
--D[x],E[x]=B[x]/D[x];
C[y]+=E[x],C[x]-=E[x]*(D[x]-1);
ins(y,x),ins(z,y);Insert(x),Insert(y),Insert(z);
A[i]=j,x=A[j],y=A[x];
Delete(j),Delete(x),Delete(y);del(y,x),del(x,j);
C[x]-=E[j],C[j]+=E[j]*(D[j]-1);
++D[j],E[j]=B[j]/D[j];
C[x]+=E[j],C[j]-=E[j]*(D[j]-1)-E[i];
ins(j,i),ins(x,j),ins(y,x);Insert(j),Insert(x),Insert(y);
}
int main(){
scanf("%d%d",&n,&q);
for(int i=1;i<=n;++i) scanf("%lld",&B[i]),D[i]=2;
for(int i=1;i<=n;++i) scanf("%lld",&A[i]),D[A[i]]++;
for(int i=1;i<=n;++i) E[i]=B[i]/D[i];
for(int i=1;i<=n;++i) C[i]+=B[i]-E[i]*(D[i]-1),C[A[i]]+=E[i];
for(int i=1;i<=n;++i) ins(A[i],i);
for(int i=1;i<=n;++i) Insert(i);
int op,x,y;
while(q--){
scanf("%d",&op);
if(op==1) scanf("%d%d",&x,&y),Modify(x,y);
else if(op==2) scanf("%d",&x),printf("%lld\n",C[x]+E[A[x]]);
else printf("%lld %lld\n",*Ans.begin(),*Ans.rbegin());
}
return 0;
}