bzoj4448: [Scoi2015]情报传递

本文介绍了一个基于主席树和树剖的算法,用于解决一个大型情报网络中的情报传递问题。该算法能够有效地计算出参与传递情报的情报员数量及对特定情报构成威胁的情报员数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

奈特公司是一个巨大的情报公司,它有着庞大的情报网络。情报网络中共有n名情报员。每名情报员口J-能有
若T名(可能没有)下线,除1名大头日外其余n-1名情报员有且仅有1名上线。奈特公司纪律森严,每
名情报员只能与自己的上、下线联系,同时,情报网络中仟意两名情报员一定能够通过情报网络传递情报。
奈特公司每天会派发以下两种任务中的一个任务:
1.搜集情报:指派T号情报员搜集情报
2.传递情报:将一条情报从X号情报员传递给Y号情报员
情报员最初处于潜伏阶段,他们是相对安全的,我们认为此时所有情报员的危险值为0;-旦某个情报员开
始搜集情报,他的危险值就会持续增加,每天增加1点危险值(开始搜集情报的当天危险值仍为0,第2天
危险值为1,第3天危险值为2,以此类推)。传递情报并不会使情报员的危险值增加。
为了保证传递情报的过程相对安全,每条情报都有一个风险控制值C。余特公司认为,参与传递这条情
报的所有情报员中,危险值大于C的情报员将对该条情报构成威胁。现在,奈特公司希望知道,对于每
个传递情报任务,参与传递的情报员有多少个,其中对该条情报构成威胁的情报员有多少个。
Input

第1行包含1个正整数n,表示情报员个数。
笫2行包含n个非负整数,其中第i个整数Pi表示i号情报员上线的编号。特别地,若Pi=0,表示i号
情报员是大头目。
第3行包含1个正整数q,表示奈特公司将派发q个任务(每天一个)。
随后q行,依次描述q个任务。
每行首先有1个正整数k。若k=1,表示任务是传递情报,随后有3个正整数Xi、Yi、Ci,依次表示传递
情报的起点、终点和风险控制值;若k=2,表示任务是搜集情报,随后有1个正整数Ti,表示搜集情报的
情报员编号。
Output

对于每个传递情报任务输出一行,应包含两个整数,分别是参与传递情报的情报员个数和对该条情报构成威胁的情报员个数。
输出的行数应等于传递情报任务的个数,每行仅包含两个整数,用一个空格隔开。输出不应包含多余的空行和空格。
Sample Input

7

0 1 1 2 2 3 3

6

1 4 7 0

2 1

2 4

2 7

1 4 7 1

1 4 7 3

Sample Output

5 0

5 2

5 1
HINT

对于3个传递情报任务,都是经过5名情报员,分别是4号、2号、1号、3号和7号。其中,对于第1个

任务,所有情报员(危险值为0)都不对情报构成威胁;对于第2个任务,有2名情报员对情报构成威胁,

分别是1号情报员(危险值为3)和4号情报员(危险值为2),7号情报员(危险值为1)并不构成威胁;

对于第3个任务,只有1名情报员对情报构成威胁。

n< = 2×10^5,Q< = 2×105,0< Pi,C!< = N, 1< = Ti,Xi,Yi< = n

题解

主席树+树剖就水过去了。。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=200005;
struct qq
{
    int x,y,last;
}e[N];int num,last[N];
void init (int x,int y)
{
    num++;
    e[num].x=x;e[num].y=y;
    e[num].last=last[x];
    last[x]=num;
}
struct qr
{
    int s1,s2;
    int c;
}s[N*30];int num1=0;
int ys[N],tot[N],son[N],fa[N],dep[N],top[N],num2;
int n,q;
void dfs (int x)
{
    tot[x]=1;
    for (int u=last[x];u!=-1;u=e[u].last)
    {
        int y=e[u].y;
        dep[y]=dep[x]+1;dfs(y);
        tot[x]+=tot[y];
        if (tot[son[x]]<tot[y]) son[x]=y;
    }
}
void dfs1 (int x,int tp)
{
    ys[x]=++num2;top[x]=tp;
    if (son[x]!=0) dfs1(son[x],tp);
    for (int u=last[x];u!=-1;u=e[u].last)
    {
        int y=e[u].y;
        if (y==son[x]) continue;
        dfs1(y,y);
    }
}
void BT ()
{
    dep[1]=1;dfs(1);
    num2=0;dfs1(1,1);
}
int root[N];
int get (int now,int l,int r,int L,int R)
{
    if (now==0) return 0;
    if (l==L&&r==R) return s[now].c;
    int mid=(l+r)>>1;
    int s1=s[now].s1,s2=s[now].s2;
    if (R<=mid) return get(s1,l,mid,L,R);
    else if (L>mid) return get(s2,mid+1,r,L,R);
    else return get(s1,l,mid,L,mid)+get(s2,mid+1,r,mid+1,R);
}
int LCA;
int  solve (int x,int y,int k)//x---->y这个路径上     有多少个有色点   用的是k 
{
//printf("%d %d %d\n",x,y,k);
    int tx=top[x],ty=top[y];
    if (dep[tx]>dep[ty]) {swap(tx,ty);swap(x,y);};//y来跳
    int ans=0;
    while (tx!=ty)
    {

        if (k>0) ans=ans+get(root[k],1,num2,ys[ty],ys[y]);
        y=fa[ty];ty=top[y];
    /*  printf("%d %d %d %d\n",x,tx,y,ty);
        system("pause");*/
        if (dep[tx]>dep[ty]) {swap(tx,ty);swap(x,y);}
    }
    if (dep[x]>dep[y]) swap(x,y);
    LCA=x;
    if (k>0) ans=ans+get(root[k],1,num2,ys[x],ys[y]);
    return ans;
}
void change (int &now,int l,int r,int x)
{
    if (now==0) now=++num1;
    s[now].c++;
    if (l==r) return ;
    int mid=(l+r)>>1;
    if (x<=mid) change(s[now].s1,l,mid,x);
    else change(s[now].s2,mid+1,r,x);
}
void Merge (int &rt1,int rt2)
{
    if (rt2==0) return ;
    if (rt1==0) {rt1=rt2;return ;}
    s[rt1].c+=s[rt2].c;
    Merge(s[rt1].s1,s[rt2].s1);
    Merge(s[rt1].s2,s[rt2].s2);
}
int main()
{
    num1=0;
    num=0;memset(last,-1,sizeof(last));
    scanf("%d",&n);
    for (int u=1;u<=n;u++)
    {
        int x;
        scanf("%d",&x);
        fa[u]=x;init(x,u);
    }
    BT();
//  for (int u=1;u<=n;u++) printf("top:%d ys:%d son:%d tot:%d dep:%d %d\n",top[u],ys[u],son[u],tot[u],dep[u],fa[u]);
    scanf("%d",&q);
    for (int u=1;u<=q;u++)
    {
        int op;
        scanf("%d",&op);
        if (op==1) //询问
        {
            int x,y,k;
            scanf("%d%d%d",&x,&y,&k);
            printf("%d %d\n",dep[x]+dep[y]-2*dep[LCA]+1,solve(x,y,u-k-1));
            root[u]=root[u-1];
        }
        else
        {
            int x;
            scanf("%d",&x);
            change(root[u],1,num2,ys[x]);
            Merge(root[u],root[u-1]);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值