JZOJ 5989. 【北大2019冬令营模拟2019.1.6】Forest

Description

Description

Input

Input

Output

Output

Sample Input

5 12
10 20 30 40 50
2 3 4 5 2
2 1
2 2
2 3
2 4
2 5
1 4 2
2 1
2 2
2 3
2 4
2 5
3

Sample Output

10
36
28
40
36
9
57
27
28
29
9 57

Data Constraint

Data Constraint
Data Constraint

Solution

  • 我们把 A [ i ] A[i] A[i] 称作 i i i 的“父亲”,而所有满足 A [ j ] = i A[j]=i A[j]=i j j j 称作 i i i 的“儿子”。

  • 观察到每个点只有 A [ i ] A[i] A[i] 这一个父亲,这是一个突破口。

  • 一开始时的 C [ i ] C[i] C[i] 我们是可以直接求出的,现在考虑快速维护 C [ i ] C[i] C[i]

  • 可以发现我们只需要一个不加上父亲权值的 C ′ [ i ] C'[i] C[i] 就很好维护了。

  • 于是一个点 i i i C [ i ] = C ′ [ i ] + B [ A [ i ] ] / D [ A [ i ] ] C[i]=C'[i]+B[A[i]]/D[A[i]] C[i]=C[i]+B[A[i]]/D[A[i]]

  • 那么修改的时候只需要在几个点上对 C ′ [ i ] C'[i] C[i] D [ i ] D[i] D[i] 加加减减即可,

  • 这个细节比较多(其实不算多了,下面更……),但很容易就做到 O ( 1 ) O(1) O(1) 进行操作1、2了。

  • 接下来就是操作3了——回答全局 C [ i ] C[i] C[i] 的最值。

  • 这个很自然会想到用 set 来维护。

  • 具体来说就是,对每个点维护一个存其儿子的 C [ i ] C[i] C[i] 的 set 。

  • 之后全局再维护一个存所有 C [ i ] C[i] C[i] 的 set ,不过这个可以将每个点的 set 中取最大最小值扔进去就够了。

  • 很“显然”我们可以在一次修改中 O ( l o g   n ) O(log\ n) O(log n) 地修改完所有 set 。。(细节巨多,无法描述。。)

  • 那么我们直接从全局 set 取出最值就可以回答询问3了。

  • 总时间复杂度 O ( n   l o g   n ) O(n\ log\ n) O(n log n)

  • 强烈建议在修改 set 的时候打成一个过程(要调用很多次),不然展开打就会像我的代码一样与臭又长……

Code

#include<cstdio>
#include<set>
#include<cctype>
using namespace std;
typedef long long LL;
const int N=1e5+5;
int a[N],d[N];
LL b[N],c[N];
multiset<LL>ss[N],val;
multiset<LL>::iterator it;
inline int read()
{
	int X=0,w=0; char ch=0;
	while(!isdigit(ch)) w|=ch=='-',ch=getchar();
	while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
	return w?-X:X;
}
int main()
{
	freopen("forest.in","r",stdin);
	freopen("forest.out","w",stdout);
	int n=read(),q=read();
	for(int i=1;i<=n;i++) scanf("%lld",&b[i]);
	for(int i=1;i<=n;i++)
	{
		a[i]=read();
		d[i]+=2;
		d[a[i]]++;
	}
	for(int i=1;i<=n;i++)
	{
		c[i]+=b[i]%d[i]+b[i]/d[i];
		c[a[i]]+=b[i]/d[i];
	}
	for(int i=1;i<=n;i++) ss[a[i]].insert(c[i]);
	for(int i=1;i<=n;i++)
		if(!ss[i].empty())
		{
			val.insert(*ss[i].begin()+b[i]/d[i]);
			val.insert(*ss[i].rbegin()+b[i]/d[i]);
		}
	while(q--)
	{
		int ty=read();
		if(ty==1)
		{
			int x=read(),y=read(),z=a[x];
			if(y==z) continue;
			if(!ss[z].empty())
			{
				it=val.find(*ss[z].begin()+b[z]/d[z]);
				if(it!=val.end()) val.erase(it);
				it=val.find(*ss[z].rbegin()+b[z]/d[z]);
				if(it!=val.end()) val.erase(it);
			}
			if(!ss[y].empty())
			{
				it=val.find(*ss[y].begin()+b[y]/d[y]);
				if(it!=val.end()) val.erase(it);
				it=val.find(*ss[y].rbegin()+b[y]/d[y]);
				if(it!=val.end()) val.erase(it);
			}
			
			ss[z].erase(ss[z].find(c[x]));
			ss[y].insert(c[x]);
			LL mn=*ss[a[z]].begin(),mx=*ss[a[z]].rbegin();
			ss[a[z]].erase(ss[a[z]].find(c[z]));
			c[z]+=b[z]%(d[z]-1)-b[z]%d[z]-b[x]/d[x]+b[z]/(d[z]-1)-b[z]/d[z];
			ss[a[z]].insert(c[z]);
			LL num=*ss[a[z]].begin();
			if(num^mn)
			{
				it=val.find(mn+b[a[z]]/d[a[z]]);
				if(it!=val.end())
				{
					val.erase(it);
					val.insert(num+b[a[z]]/d[a[z]]);
				}
			}
			num=*ss[a[z]].rbegin();
			if(num^mx)
			{
				it=val.find(mx+b[a[z]]/d[a[z]]);
				if(it!=val.end())
				{
					val.erase(it);
					val.insert(num+b[a[z]]/d[a[z]]);
				}
			}
			mn=*ss[a[a[z]]].begin(),mx=*ss[a[a[z]]].rbegin();
			ss[a[a[z]]].erase(ss[a[a[z]]].find(c[a[z]]));
			c[a[z]]+=b[z]/(d[z]-1)-b[z]/d[z];
			ss[a[a[z]]].insert(c[a[z]]);
			num=*ss[a[a[z]]].begin();
			if(num^mn)
			{
				it=val.find(mn+b[a[a[z]]]/d[a[a[z]]]);
				if(it!=val.end())
				{
					val.erase(it);
					val.insert(num+b[a[a[z]]]/d[a[a[z]]]);
				}
			}
			num=*ss[a[a[z]]].rbegin();
			if(num^mx)
			{
				it=val.find(mx+b[a[a[z]]]/d[a[a[z]]]);
				if(it!=val.end())
				{
					val.erase(it);
					val.insert(num+b[a[a[z]]]/d[a[a[z]]]);
				}
			}
			d[z]--;
			
			a[x]=y;
			mn=*ss[a[y]].begin(),mx=*ss[a[y]].rbegin();
			ss[a[y]].erase(ss[a[y]].find(c[y]));
			c[y]+=b[y]%(d[y]+1)-b[y]%d[y]+b[x]/d[x]+b[y]/(d[y]+1)-b[y]/d[y];
			ss[a[y]].insert(c[y]);
			num=*ss[a[y]].begin();
			if(num^mn)
			{
				it=val.find(mn+b[a[y]]/d[a[y]]);
				if(it!=val.end())
				{
					val.erase(it);
					val.insert(num+b[a[y]]/d[a[y]]);
				}
			}
			num=*ss[a[y]].rbegin();
			if(num^mx)
			{
				it=val.find(mx+b[a[y]]/d[a[y]]);
				if(it!=val.end())
				{
					val.erase(it);
					val.insert(num+b[a[y]]/d[a[y]]);
				}
			}
			mn=*ss[a[a[y]]].begin(),mx=*ss[a[a[y]]].rbegin();
			ss[a[a[y]]].erase(ss[a[a[y]]].find(c[a[y]]));
			c[a[y]]+=b[y]/(d[y]+1)-b[y]/d[y];
			ss[a[a[y]]].insert(c[a[y]]);
			num=*ss[a[a[y]]].begin();
			if(num^mn)
			{
				it=val.find(mn+b[a[a[y]]]/d[a[a[y]]]);
				if(it!=val.end())
				{
					val.erase(it);
					val.insert(num+b[a[a[y]]]/d[a[a[y]]]);
				}
			}
			num=*ss[a[a[y]]].rbegin();
			if(num^mx)
			{
				it=val.find(mx+b[a[a[y]]]/d[a[a[y]]]);
				if(it!=val.end())
				{
					val.erase(it);
					val.insert(num+b[a[a[y]]]/d[a[a[y]]]);
				}
			}
			d[y]++;
			
			if(!ss[z].empty())
			{
				val.insert(*ss[z].begin()+b[z]/d[z]);
				val.insert(*ss[z].rbegin()+b[z]/d[z]);
			}
			if(!ss[y].empty())
			{
				val.insert(*ss[y].begin()+b[y]/d[y]);
				val.insert(*ss[y].rbegin()+b[y]/d[y]);
			}
		}else
		if(ty==2)
		{
			int x=read();
			printf("%lld\n",c[x]+b[a[x]]/d[a[x]]);
		}else
		{
			printf("%lld %lld\n",*val.begin(),*val.rbegin());
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值