传送门题目可以qq找我要
每个点只有一个出点。维护权值c。
同样我们从修改对答案的影响这个角度来思考问题。
如果我修改了一个点的出边,就会修改度数,修改度数就会修改E。而修改E会修改这个点周围一圈的所有C。
这号rilong啊,,复杂度爆炸。
抓住出边为1这个条件。再将被影响的点分类。
父亲:单点修改,我:单点修改,儿子们:一大群,我们可以找个什么东西来维护。
我们发现权值的计算公式是加法,所以我们大可以先将所有儿子的c中减去我的E。计算答案的时候加回来就是。
我们成功让我的E修改与儿子无关啦。
问题说是求最大最小。能维护最大最小最方便的数据结构(最好是STL)是啥呢,,当然是set啦。
multiset嘛。
确定了数据结构,我们再从头思考一下每次修改的影响。
我和父亲断边,影响了父亲的D,E,C。因为影响了父亲的E,所以影响了祖父的C。
而在set上,我影响了父亲,祖父和曾祖父。这三个人set都会发生变化。
顺带维护两个set,一个最大一个最小。最大就是所有点set的最大。最小就是所有set的最小。
所以在修改的时候也要把答案先删除。最后插入。
然后就没了,,疯狂set就是了。
//a:指向谁。
//b:糖果数
//c:不包含父亲的权值。
//d:度数+自己
//e:b/d
//s:维护儿子的权值(注意儿子权值没有包含自己)。
#include<bits/stdc++.h>
using namespace std;
#define in read()
#define int long long
int in{
int cnt=0,f=1;char ch=0;
while(!isdigit(ch)){
ch=getchar();if(ch=='-')f=-1;
}
while(isdigit(ch)){
cnt=cnt*10+ch-48;
ch=getchar();
}return cnt*f;
}
int n,m;
const int N=1e5+3;
int a[N],b[N],c[N],d[N],e[N];
multiset<int> q[N],minn,maxx;
multiset<int>::iterator it; //留作纪念的指针qwq
//x原来指向z,现在指向y。
//z的度数-1,e改变,c改变,set去掉该子节点。
//x度数不变,e不变,c改变,指向改变。
//y度数+1,e改变,c改变,set加上x。
//minn,maxx改变,z,y先删再加。
//
//void modify(int x,int y){
// if(a[x]==y)return;
// c[a[x]]+=d[a[x]]*e[a[x]];
// d[x]--;d[a[x]]--;int z=a[x];
// c[z]-=e[x];c[z]=-=e[z];e[z]=b[z]/d[z];c[z]+=e[z];
// c[z]-=d[z]*e[z];q[z].erase((q[z].lower_bound(c[x])));
//}
//尝试拆分问题
//删除最大最小贡献。修改一个点会影响其父亲的dec,祖父的c。所以会影响其父,祖父,曾祖父 三人的set。全部删除。
inline void modify(int to,int E,int C,int inv){
if(!q[to].empty()){
minn.erase(minn.lower_bound((*q[to].begin())+e[to]));
maxx.erase(maxx.lower_bound((*--q[to].end())+e[to]));
}
if(inv==-1)q[to].erase(q[to].lower_bound(C)); //删去此儿子。
//此处注意顺序,先删除对答案贡献,再剔除内部信息。
minn.erase(minn.lower_bound((*q[a[to]].begin())+e[a[to]])); //祖父
minn.erase(minn.lower_bound((*q[a[a[to]]].begin())+e[a[a[to]]])); //祖父
maxx.erase(maxx.lower_bound((*--q[a[to]].end())+e[a[to]])); //曾祖父
maxx.erase(maxx.lower_bound((*--q[a[a[to]]].end())+e[a[a[to]]])); //曾祖父
q[a[to]].erase(q[a[to]].lower_bound(c[to])); //祖父
q[a[a[to]]].erase(q[a[a[to]]].lower_bound(c[a[to]])); //曾祖父
//按照从上到下的顺序消除贡献,再从下到上增加贡献。
c[a[to]]-=e[to];c[to]-=b[to]-d[to]*e[to]+e[to];
d[to]+=inv;e[to]=b[to]/d[to];c[to]+=b[to]-d[to]*e[to]+e[to];c[to]+=inv*E;c[a[to]]+=e[to];
//更新minn,maxx。注意顺序,先加入内部信息,再贡献答案
q[a[to]].insert(c[to]);q[a[a[to]]].insert(c[a[to]]);
minn.insert((*q[a[to]].begin())+e[a[to]]);
minn.insert((*q[a[a[to]]].begin())+e[a[a[to]]]);
maxx.insert((*--q[a[to]].end())+e[a[to]]);
maxx.insert((*--q[a[a[to]]].end())+e[a[a[to]]]);
if(inv==1)q[to].insert(C); //加上此儿子
if(!q[to].empty()){
minn.insert((*q[to].begin())+e[to]);
maxx.insert((*--q[to].end())+e[to]);
}
}
signed main(){
//freopen("forest.in","r,",stdin);
//freopen("forest.out","w",stdout);
n=in;m=in;//cout<<"@#@# "<<m<<endl;
for(register int i=1;i<=n;i++)b[i]=in,d[i]++;
for(register int i=1;i<=n;i++)a[i]=in,d[a[i]]++,d[i]++;
for(register int i=1;i<=n;i++)e[i]=b[i]/d[i],c[a[i]]+=e[i];
for(register int i=1;i<=n;i++)c[i]+=b[i]-d[i]*e[i]+e[i];
for(register int i=1;i<=n;i++)q[a[i]].insert(c[i]);
for(register int i=1;i<=n;i++){//注意,c没包含父亲,所以当前set没装自己的e。
if(!q[i].empty())minn.insert((*q[i].begin())+e[i]),maxx.insert((*--q[i].end())+e[i]);//xxx
} int op,x,y;
while(m){--m;
op=in;
if(op==3){
cout<<(*minn.begin())<<" "<<(*--maxx.end())<<'\n';
}
else if(op==2){
x=in;cout<<c[x]+e[a[x]]<<'\n';
}else if(op==1){
x=in;y=in;
if(a[x]==y)continue;
modify(a[x],e[x],c[x],-1);
a[x]=y;
modify(a[x],e[x],c[x],1);
}
}
return 0;
}
/*
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
*/