JZOJ 4811. 排队(线段树的方法)

Problem

Description

这里写图片描述

Input

这里写图片描述

Output

这里写图片描述

Sample Input

5 4
1 2
1 3
3 4
3 5
1 4
2 4
1 2
2 5

Sample Output

3
1
1
2

Data Constraint

这里写图片描述
这里写图片描述

Hint

这里写图片描述

Solution

之前有很多人用了来解决这个问题,现在我来讲一下线段树的方法。
我们先按照题意弄出一个优先序列order(就是先进哪个房间)。这个其实很好搞。
数组模拟链表一开始head显示的边是后面加进来的边,那么我们想先遍历的点的编号越小,那么我们可以按照这些边从大到小排,这样的话从head开始搜的边就是最小的,next之后越来越大(因为先加编号大的边,后加编号小的边)。
我们按order建立一棵线段树,来维护每段区间有多少个空位
先说操作2吧
对于每一个x,我们要找到它的一个祖先fa,这个祖先fa的父亲是空的。然后x与fa的深度差就是答案。然后fa这个房间空出来。下图是我对这个步骤的解释。
这里写图片描述
不要忘记将fa要在线段树里面空掉!
关键是操作1
我们要将order前面x个空的房间补满。我们尽量往左补,如果能够补完就补,不能补完就往右补直到补完为止。

void modify(int s,int l,int r,int x,bool p)
{
    if (l==r)
    {
        tree[s]=0;
        b[o[l]]=1;
        if (p) mn=l;
        return;
    }
    int wz=(l+r)/2,prd=tree[s*2];
    if (prd>=x/*左边有空位*/) modify(s*2,l,wz,x,p);
        else
        {
            modify(s*2,l,wz,prd,0);//将之前的空补满
            modify(s*2+1,wz+1,r,x-prd,p);//向右搜
        }
    tree[s]=tree[s*2]+tree[s*2+1];
}

至于什么倍增等的基础算法自己去实现。

Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#define N 100010
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
#define edge(i,x) for(i=head[x];i;i=next[i])
using namespace std;
int LCA,ans,x,y,i,j,cnt,n,t,op,tot,mn;
int c[N*2][2],o[N],o1[N],deep[N],go[N*2],next[N*2],head[N*2],f[N][20],tree[N*10];
bool b[N];
void lb(int x,int y)
{
    go[++tot]=y;
    next[tot]=head[x];
    head[x]=tot;
}
void bs(int x)
{
    int i,now;
    deep[x]=deep[f[x][0]]+1;
    fo(i,1,log2(n)) if (f[x][i-1]) f[x][i]=f[f[x][i-1]][i-1];
    edge(i,x)
    {
        now=go[i];
        if (now==f[x][0]) continue;
        f[now][0]=x;
        bs(now);
    }
    o[++o[0]]=x;
}
void build(int s,int l,int r)
{
    if (l==r)
    {
        tree[s]=1;
        return;
    }
    int wz=(l+r)/2;
    build(s*2,l,wz);
    build(s*2+1,wz+1,r);
    tree[s]=tree[s*2]+tree[s*2+1];
}
void modify(int s,int l,int r,int x,bool p)
{
    if (l==r)
    {
        tree[s]=0;
        b[o[l]]=1;
        if (p) mn=l;
        return;
    }
    int wz=(l+r)/2,prd=tree[s*2];
    if (prd>=x) modify(s*2,l,wz,x,p);
        else
        {
            modify(s*2,l,wz,prd,0);
            modify(s*2+1,wz+1,r,x-prd,p);
        }
    tree[s]=tree[s*2]+tree[s*2+1];
}
void change(int s,int l,int r,int x)
{
    if (l==r)
    {
        tree[s]=1;
        return;
    }
    if (l==r) return;
    int wz=(l+r)/2;
    if (x<=wz) change(s*2,l,wz,x);
          else change(s*2+1,wz+1,r,x);
    tree[s]=tree[s*2]+tree[s*2+1];      
}
void qsort(int l,int r)
{
    int i=l,j=r,m0=c[(l+r)/2][0],m1=c[(l+r)/2][1];
    while (i<j)
    {
        while (c[i][0]>m0 || c[i][0]==m0 && c[i][1]>m1) i++;
        while (c[j][0]<m0 || c[j][0]==m0 && c[j][1]<m1) j--;
        if (i<=j)
        {
            swap(c[i],c[j]);
            i++;j--;
        }
    }
    if (l<j) qsort(l,j);
    if (i<r) qsort(i,r);
}
int main()
{
    scanf("%d%d",&n,&t);
    fo(i,1,n-1)
    {
        scanf("%d%d",&c[i][0],&c[i][1]);
        if (c[i][0]>c[i][1]) swap(c[i][0],c[i][1]);
    }
    qsort(1,n-1);
    fo(i,1,n-1) lb(c[i][0],c[i][1]),lb(c[i][1],c[i][0]);
    bs(1);
    fo(i,1,n) o1[o[i]]=i;
    build(1,1,n);
    fo(i,1,t)
    {
        scanf("%d%d",&op,&x);
        if (op==1)
        {
            mn=0;
            modify(1,1,n,x,1);
            printf("%d\n",o[mn]);
        } else
        {
            LCA=x;
            fd(j,log2(n),0)
                if (b[f[LCA][j]]) LCA=f[LCA][j];
            b[LCA]=0;
            ans=deep[x]-deep[LCA];
            printf("%d\n",ans);
            change(1,1,n,o1[LCA]);
        }
    }
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值