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

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
#include
#include
#include
#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]typedef set :: iterator itn;
set 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;
        }
    }
}

你可能感兴趣的:(STL,set,倍增算法)