XJOI NOIP2016提高组冲剌题1 T3:排队(堆+倍增)

本文解析了XJOINOIP2016提高组冲刺题1T3:排队问题,通过堆和倍增算法优化操作效率,解决了在结点处放置人及统计影响人数的问题。

XJOI NOIP2016提高组冲剌题1 T3:排队




题目分析:

此题有两种操作:在结点处放置人,人被拿走后的统计影响的人数.
其实观察可以发现,第二种操作实际上就是讲将该点以上到第一个为空的祖先以下的所有点向下移动一个,倍增就很好解决,关键在于操作一的实现,暴力实现的话,需要把某一个人从根开始向下模拟放置,最坏情况下需要遍历整棵树.但是可以发现,实际上放置结点的顺序是一定的,即将人放置在某一个结点上,必定有放置顺序在它前面的点都已被放置,那么可以用堆来实现这个操作,先深搜一遍,求出其优先级,在堆中存放空结点即可.

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>

using namespace std;

const int logn=18;
const int maxn=100000+10;

int read()
{
    char ch=getchar();int ret=0,flag=1;
    while(ch<'0'||ch>'9') {if(ch=='-') flag=-1; ch=getchar();}
    while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    return ret*flag;
}

vector<int>G[maxn];
int P[maxn],inq[maxn],pcnt;//P[i]表示i结点的优先级(此处值小的更优)
int anc[maxn][logn+2],n;

void dfs(int u,int fa)
{
    anc[u][0]=fa;
    int d=G[u].size();
    for(int i=0;i<d;i++) if(G[u][i]!=fa) {
        int v=G[u][i];dfs(v,u);
    }
    P[u]=++pcnt;
}

void init()
{
    for(int i=1;i<=logn;i++)
        for(int j=1;j<=n;j++)
            anc[j][i]=anc[anc[j][i-1]][i-1];
}

int find(int x,int& ans)//倍增求第一个空祖先以前的结点数
{
    for(int i=logn;x!=1&&i>=0;i--) if(anc[x][i]&&!inq[anc[x][i]])
        x=anc[x][i],ans+=(1<<i);
    return x;
}

struct Node {
    int id;
    Node(){}
    Node(int id):id(id){}
    bool operator < (const Node& rhs) const {
        return P[id]>P[rhs.id];
    }
};

int main()
{
    n=read();int t=read();
    for(int u,v,i=1;i<n;i++) {//用vector存储,方便排序
        u=read();v=read();
        G[u].push_back(v);
        G[v].push_back(u);
    }
    for(int i=1;i<=n;i++) sort(G[i].begin(),G[i].end());
    dfs(1,0);init();//深搜求优先级以及倍增初始化
    priority_queue<Node>q;
    for(int i=1;i<=n;i++) q.push(Node(i)),inq[i]=1;
    while(t--) {
        int op=read(),x=read(),ans=0;
        if(op==1) {
            Node u;
            while(x--) {
                u=q.top();q.pop();
                inq[u.id]=0;
            }
            ans=u.id;
        }
        else {
            int p=find(x,ans);
            q.push(Node(p));inq[p]=1;
        }
        printf("%d\n",ans);
    }
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值