排队

题目描述

这里写图片描述

做法

我们先处理出fix表示在全空情况下不断进人,i这个房间被第几个人最终停留。
用一个堆保留空房间当前fix的最小值,每次进一个人就是选择堆中最小值的房间。
拿走人的话观察一下就是从这个房间往上有人房间的数量-1,这个可以倍增来求,因为这个房间到根路径上有人和没人一定是分离开的。
分析一下势能这样做是n log n的。

#include<cstdio>
#include<algorithm>
#include<set>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=100000+10;
int f[maxn][25],fix[maxn],co[maxn],d[maxn],b[maxn*2],sta[100];
int h[maxn],go[maxn*2],next[maxn*2];
struct dong{
    int id,fix;
    friend bool operator <(dong a,dong b){
        return a.fix<b.fix;
    }
};
set<dong> s;
dong zlt;
int i,j,k,l,t,n,m,tot,top;
int read(){
    int x=0;
    char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x;
}
void add(int x,int y){
    go[++tot]=y;
    next[tot]=h[x];
    h[x]=tot;
}
void dfs(int x,int y){
    f[x][0]=y;
    d[x]=d[y]+1;
    int i,t=h[x],l=top+1,r=top;
    while (t){
        if (go[t]!=y) b[++r]=go[t];
        t=next[t];
    }
    sort(b+l,b+r+1);
    top=r;
    fo(i,l,r) dfs(b[i],x);
    top=l-1;
    fix[x]=++tot;
}
int getfather(int x){
    int j=floor(log(n)/log(2));
    while (j>=0){
        if (co[f[x][j]]==1) x=f[x][j];
        j--;
    }
    return x;
}
void write(int x){
    if (!x){
        putchar('0');
        putchar('\n');
        return;
    }
    top=0;
    while (x){
        sta[++top]=x%10;
        x/=10;
    }
    while (top){
        putchar('0'+sta[top]);
        top--;
    }
    putchar('\n');
}
int main(){
    n=read();m=read();
    fo(i,1,n-1){
        j=read();k=read();
        add(j,k);add(k,j);
    }
    top=tot=0;
    dfs(1,0);
    fo(j,1,floor(log(n)/log(2)))
        fo(i,1,n)
            f[i][j]=f[f[i][j-1]][j-1];
    fo(i,1,n){
        zlt.id=i;
        zlt.fix=fix[i];
        s.insert(zlt);
    }
    while (m--){
        t=read();
        if (t==1){
            k=read();
            while (k--){
                zlt=*s.begin();
                co[zlt.id]=1;
                if (!k) write(zlt.id);
                s.erase(s.find(zlt));
            }
        }
        else{
            k=read();
            if (!co[k]){
                write(0);
                continue;
            }
            j=getfather(k);
            write(d[k]-d[j]);
            co[j]=0;
            zlt.id=j;
            zlt.fix=fix[j];
            s.insert(zlt);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值