「Codeforces 643D」Bearish Fanpages

传送门


problem

A 所在的地区有 n n n 个点,标号为 1 ∼ n 1 ∼ n 1n。每个节点都连出去恰好一条有向边,设 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=BiDi×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 3n105 1 ≤ q ≤ 1 0 5 1 ≤ q ≤ 10^5 1q105 1 ≤ B i ≤ 1 0 12 1 ≤ B_i ≤ 10^{12} 1Bi1012 1 ≤ A i ≤ n 1 ≤ A_i ≤ n 1Ain


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={CjAj=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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值