【NOIP2016提高A组五校联考1】排队

5 篇文章 0 订阅
4 篇文章 0 订阅

Description

给出一棵n个节点以1为根的树,和m次操作。
每次操作把x个人扔进这棵树。
每个人会选择当前节点中一个没有人且编号最大的节点走过去。
问最后一个人停在哪个节点。
或者把x这个节点的人删除,把它上面的所有人往下移一格,问移动了多少人。
n,m<=10^5

Solution

你有木有觉得这个走的方法很像dfs序的遍历?
那么我们可以构造出这个特殊的dfs序,不过每次我们选择编号大的先走。
这东西你可以用set/vector/map/queue等等黑科技来做。
(p党伤不起就去转c吧)
这样子就可以保证这个序列从后往前就是我们要插入的顺序了。
于是我们可以给每个点一个优先级,然后把它们按优先级排序。
同时要兹瓷插入删除。。
辣么用set/queue/heap就好辣
p党:卒

操作二就倍增往上跳到最上面一个有人的节点。
因为不可能一个节点没人但他的父辈有人。

然后就解决啦(黑科技大全纪念)

Code

#include<set>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define N 100005
using namespace std;
typedef set<int> :: iterator its;
set<int> s[N];
int dfn[N],fa[N][17],d[N],tot,n,m,x,y,z;
bool bz[N],in[N];
struct note{
    int id;
    friend bool operator < (note x,note y) {
        return dfn[x.id]<dfn[y.id];
    }
};
typedef set<note> :: iterator itn;
set<note> q;
void dfs(int x,int y) {
    dfn[x]=++tot;fa[x][0]=y;d[x]=d[y]+1;
    its it=s[x].end();bool pd=0;
    do {
        it--;
        if (*it!=y) pd=1,dfs(*it,x);
    } while (it!=s[x].begin());
    note z;z.id=x;
    if (!pd) q.insert(z),in[x]=1;
}
int jump(int x) {
    fd(j,16,0) if (bz[fa[x][j]]) x=fa[x][j]; 
    return x;
}
int main() {
    scanf("%d%d",&n,&m);
    fo(i,1,n-1) scanf("%d%d",&x,&y),s[x].insert(y),s[y].insert(x);
    dfs(1,0);
    fo(j,1,16) fo(i,1,n) fa[i][j]=fa[fa[i][j-1]][j-1];
    for(;m;m--) {
        scanf("%d%d",&z,&x);
        if (z==1) {
            fo(i,1,x) {
                itn it=q.end();it--;
                note v=*it;bz[v.id]=1;
                q.erase(it);
                if (!in[fa[v.id][0]]) {
                    in[fa[v.id][0]]=1;
                    note z;z.id=fa[v.id][0];
                    q.insert(z);
                }
                if (i==x) printf("%d\n",v.id);
            }
        } else {
            int v=jump(x);
            printf("%d\n",d[x]-d[v]);
            note z;z.id=v;
            q.insert(z);
            in[v]=1;bz[v]=0;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值