CodeForces 643 D.Bearish Fanpages(set+multiset)

Description

一个社交网站有 n n 个网页,第i个公司拥有第 i i 个网页,每一个网页有一个父网页,不允许i网页是 j j 网页的父网页且j网页是 i i 网页的父网页,也不允许i网页是自己的父网页,对于 i i 网页,设其父网页是j0网页,其子网页为 j1,j2,...,jk j 1 , j 2 , . . . , j k ,当用户浏览 i i 网页时,他们会看到来自k+2家公司 i,j0,j1,...,jk i , j 0 , j 1 , . . . , j k 的广告,有 ti t i 个用户喜欢第 i i 个网页,他们每个人都会点开一个广告看,对于k+1家公司 j0,j1,...,jk j 0 , j 1 , . . . , j k ,会有 tik+2 ⌊ t i k + 2 ⌋ 个用户点开他们家的广告,对于剩下的 ti(k+1)tik+2 t i − ( k + 1 ) ⌊ t i k + 2 ⌋ 个用户,他们会点开第 i i 家公司的广告。一个公司的总收入等于看他们家广告的用户数量。现在给出第i个网页的父网页 fi f i ,有 q q 个操作,操作分三种:

1 i j:第 i i 个网页的父网页变成j,保证之前第 i i 个网页的父网页不是j

2 i 2   i :查询第 i i 家公司的总收入

3:输出这 n n 家公司的最少收入和最多收入

Input

第一行两整数n q q 分别表示公司数量和操作数量,之后输入n个整数 t1,...,tn t 1 , . . . , t n 表示喜欢第 i i 个网页的用户数量,之后输入n个整数 f1,...,fn f 1 , . . . , f n 表示第 i i 个网页的父网页,最后q行每行一个操作 (3n105,1q105,1ti1012) ( 3 ≤ n ≤ 10 5 , 1 ≤ q ≤ 10 5 , 1 ≤ t i ≤ 10 12 )

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

Solution

对每个节点 x x 用一个set维护其儿子节点,维护儿子节点数量 num[x] n u m [ x ] ,维护该节点其所有儿子节点对它的贡献之和 ans[x] a n s [ x ] ,那么一个节点的总收入为 ans[x] a n s [ x ] + f[x] f [ x ] x x 的贡献

总收入的最值必然是某个节点的儿子达到的,故开一个multiset S维护每个节点的儿子节点最值+自身对儿子的贡献,即为儿子真实总收入最值

对于 1 1 操作,假设x的父亲由 f[x] f [ x ] 变成 y y ,那么ans值改变的最多只有 x,f[x],f[f[x]],y,f[y] x , f [ x ] , f [ f [ x ] ] , y , f [ y ] ,对应的要把其父亲节点的儿子节点最值从 S S 中删掉,修改对应的ans值后再把这些点的父亲节点的儿子节点最值插入到 S S 中完成修改,注意这些点可能有重复,重复的点只需修改一次

对于2操作,只需要输出 ans[x] a n s [ x ] + f[x] f [ x ] x x 的贡献即为x节点的真实总收入

对于 3 3 操作,从S中选出最值即可

Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
#define maxn 1000005
multiset<ll>S;
int n,q,p[maxn];
ll t[maxn],ans[maxn];
struct cmp
{
    bool operator()(int a,int b)
    {
        if(ans[a]!=ans[b])return ans[a]<ans[b];
        return a<b;
    }
};
//set<int,cmp>::iterator it;
struct node
{
    int num;//儿子节点的数量
    set<int,cmp>s;
    vector<ll> get()//取出儿子中最小值和最大值
    {
        vector<ll>v;
        if(!s.empty())v.push_back(ans[*s.begin()]);
        if(s.size()>=2)
        {
            auto it=s.end();
            it--;
            v.push_back(ans[*it]);
        }
        return v;
    }
    void insert(int a)
    {
        s.insert(a);
    }
    void erase(int a)
    {
        s.erase(a);
    }
}f[maxn];
ll other(int a)
{
    return t[a]/(2+f[a].num);
}
ll self(int a)
{
    return t[a]-(1+f[a].num)*other(a);
}
int main()
{
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)scanf("%I64d",&t[i]);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&p[i]);
        f[p[i]].num++,f[p[i]].insert(i);
    }
    for(int i=1;i<=n;i++)
    {
        //必须要先删掉i再插入i,因为ans[i]的值已经改变,其在set中的位置也应改变 
        f[p[i]].erase(i);
        ans[i]=self(i);
        for(int x:f[i].s)ans[i]+=other(x);
        f[p[i]].insert(i);
    }
    for(int i=1;i<=n;i++)
    {
        vector<ll> V=f[i].get();
        for(ll v:V)S.insert(v+other(i));
    }
    //for(int i=1;i<=n;i++)printf("%I64d ",ans[i]+other(p[i]));
    while(q--)
    {
        int op;
        scanf("%d",&op);
        if(op==1)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            set<int>update=set<int>{a,p[a],p[p[a]],b,p[b]};
            set<int>update1=update;
            for(int x:update)update1.insert(p[x]);
            for(int x:update1)
            {
                vector<ll> V=f[x].get();
                for(ll v:V)
                {
                    auto it=S.find(v+other(x));
                    if(it!=S.end())S.erase(it);
                }
            }
            for(int x:update)f[p[x]].erase(x);
            for(int state=-1;state<=1;state+=2)
            {
                for(int x:update)
                {
                    ans[x]+=state*self(x);
                    for(int y:update)
                        if(p[y]==x)ans[x]+=state*other(y);
                }
                if(state==-1)
                    f[p[a]].num--,p[a]=b,f[b].num++;
            }
            for(int x:update)f[p[x]].insert(x);
            for(int x:update1)
            {
                vector<ll> V=f[x].get();
                for(ll v:V)S.insert(v+other(x));
            }
        }
        else if(op==2)
        {
            int a;
            scanf("%d",&a);
            printf("%I64d\n",ans[a]+other(p[a]));
        }
        else
        {
            printf("%I64d ",*S.begin());
            auto it=S.end();
            it--;
            printf("%I64d\n",*it);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值