zoukankan      html  css  js  c++  java
  • 【2018.06.26NOIP模拟】T3节目parade 【支配树】*

    【2018.06.26NOIP模拟】T3节目parade


    题目描述

    学校一年一度的学生艺术节开始啦!在这次的艺术节上总共有 N 个节目,并且总共也有 N 个舞台供大家表演。其中第 i 个节目的表演时间为第 i 个单位时间,表演的舞台为 Ai ,注意可能有多个节目使用同一个舞台。作为 Tom 的忠实粉丝之一的 Alice,当然要来逛一下啦,顺便看一下能不能要到 Tom 的签名。

    Alice 一开始会先在 A1 看完节目1再去闲逛。

    Alice 可以在舞台之间随便乱走。但是假如 Alice 当前在看第 i 个节目,站在第 Ai 个舞台前面的话,由于有些道路被封锁了,所以Alice 下一步只能前往第 Li~Ri 个舞台中的一个。并且当一个节目结束的时候,Alice 只能去看另外一个节目,或者结束自己的闲逛。

    具体而言就是说,假设 Alice 可以从第 i 个节目走去第 j 个节目,那么当且仅当i<jLiAjRi

    但事实上是,Tom 非常讨厌被自己的粉丝跟踪。所以他想在只封锁掉一个节目的情况下,使得 Alice 不能到达自己所在的地方。并且为了防止意外,他还想知道有多少个这样的节目。

    简而言之,Tom 想知道对于任意一个节目 p∈[1,N] ,有多少个节目 t ,使得删掉 t 之后,不存在一条从 节目1 出发到 节目p 的路径。注意,节目1 和 节目p 也是可以被删的。由于他非常的忙碌,所以他把这个任务交给了你。

    输入格式

    第一行包括一个正整数 N ,表示总共有 N 个节目。
    第二行包括 N 个正整数 Ai ,表示第 i 个节目占用了第 Ai 个舞台。
    接下来的 N 行,第 i 行包括两个正整数 Li,Ri,表示第 i 个节目的路径限制。

    输出格式
    总共 N 行。第 i 行包括一个整数 c ,表示当 Tom 站在第 i 个节目的时候,有多少个满足要求的节点。
    特别的,若一开始就不存在从 1 出发到 i 的路径的话,你需要输出 -1 。

    输入

    10
    1 6 1 8 7 2 3 9 10 10
    5 8
    2 4
    1 2
    9 10
    8 9
    9 9
    10 10
    2 2
    2 4
    9 9

    输出

    1
    2
    -1
    2
    2
    3
    3
    2
    2
    2

    备注

    【样例解释】
    我们假如将一个节目视为一个节点的话,按题意所述,我们可以构造出一副有向图。
    这里写图片描述

    设对于点i,他可选的删除集合为 Si,那么很直观的就可以看出来:
    对于1号节点,S1 = {1}
    对于2号节点,S2 = {1,2}
    对于3号节点,由于本来就不存在 1 到 3 的路径,所以应输出 -1
    对于4号节点,S4 = {1,4}
    对于5号节点,S5 = {1,5}
    对于6号节点,S6 = {1,2,6}
    对于7号节点,S7 = {1,2,7}
    对于8号节点,S8 = {1,8},5 号点和 6 号点都不是合法的点。
    对于9号节点,S9 = {1,9}
    对于10号节点,S10 = {1,10}

    【数据范围】
    对于 15% 的数据,N≤100
    对于 30% 的数据,N≤800
    对于 50% 的数据,N≤5000
    对于 70% 的数据,N≤10000
    对于 100% 的数据,N≤50000


    考试的时候果断写了一个O(n2)的暴力搞到了30分,但是看正解什么Dominator Tree内心是炸裂的,后面仔细研究了一下标程,大概搞懂了是什么意思

    我们可以希望处理出1到一个点的路径上的所有必经点,那么我们记录一下到一个点i上的距离i的最近必经点idom,所以对于一个i答案就是路径上所有节点的最近必经点的并集,又因为idom是i在dominator tree上的所有祖先的集合(dominator tree根据必须经过关系建树),所以idim就是在路径上所有点在dominator tree上的LCA,然后我们就可以用top排序跑一边就好了,然后我们就可以O((N+E)log(N))得到答案,其中N和E分别为Dag中的点数和边数

    然后考虑原题中的(Li,Ri)限制,然后我们可以建立一颗线段树,那我们考虑怎么对题目要求的关系进行构图,设线段树Ti的一个区间储存了i<=j,L<=Aj<=R的点集Si,Li,Ri,那么对于一个新的点i,相当于连到了Ti+1(Li,Ri)的所有点,那么我们对于每一个新的节点,我们建立节点Pi,Li,Ri,并且让P连接到S集合中的所有点,那么我们考虑i号节点,我们在线段树中将(Li,Ri)拆分成若干个区间(lj,rj)那么我们只需要把Pi,Li,Ri连接到Pi+1,lj,rj,这样维护显然原图联通关系并不会受到影响。
    那么考虑怎么维护所有的T,显然,如果使用静态储存方式,我们肯定会MLE,所以我们采取可持久化线段树的思想,进行维护。
    那么假如现在要向Ti,L,R中加入一个j,那么我们需要把Pi,L,R连接到j,同样的,我们还需要把Pi,L,R连接到剩下的属于Si,L,R的节点,又因为我们可以发现Si,L,RSi+1,L,R=j所以我们只需要把Pi,L,R连接到Pi+1,L,R
    那么我们就可以发现原Dag中的连通性是不会改变的

    来分析一下时间复杂度:
    原图中有n个点,对于每个点建线段树的时候我们只会加入log(n)个点,所以点数只有nlog(n)个,同样的,我们发现对于每一个点建树时只会增加2log(n)条边,所以总边数是nlog(n)规模的,按照dominator tree的时间复杂度,我们发现这道题的总复杂度是O((nlogn)log(nlogn))

    还有就是跑lca的时候边界写挂了,下次应该注意边界问题


    #include<bits/stdc++.h>
    using namespace std;
    #define N 1000010
    int n,ind=0; 
    int a[N],l[N],r[N],rt[N];
    struct Node{int l,r;}t[N];
    struct Edge{int v,next;};
    struct Dominator_Tree{
        bool vis[N];
        int head[N],tot;
        int fa[N][31],dep[N];
        int ans[N],cnt[N];
        Edge E[N<<1];
        void add(int u,int v){
            E[++tot]=(Edge){v,head[u]};
            head[u]=tot;
        }
    //  int lca(int x,int y){
    //      if(dep[x]<dep[y])swap(x,y);
    //      int t=dep[x]-dep[y];
    //      for(int i=30;i;i--)
    //          if(t&(1<<i))x=fa[x][i];
    //      if(x==y)return x;
    //      for(int i=30;i>=0;i--)
    //          if(fa[x][i]!=fa[y][i]) 
    //              x=fa[x][i],y=fa[y][i];
    //      return fa[x][0];
    //  }
        int lca(int x,int y) {
            if(dep[x]<dep[y])swap(x,y);
            int t=dep[x]-dep[y];
            for(int i=30;i>=0;i--)
                if(t&(1<<i))x=fa[x][i];
            if(x==y)return x;
            for(int i=30;i>=0;i--)
                if(fa[x][i]!=fa[y][i]) 
                    x=fa[x][i],y=fa[y][i];
            return fa[x][0];
        }
        void bfs(){
            memset(cnt,0,sizeof(cnt));
            queue<int> q;q.push(1);
            vis[1]=1;
            while(!q.empty()){
                int u=q.front();q.pop();
                for(int i=head[u];i;i=E[i].next){
                    int v=E[i].v;
                    cnt[v]++;
                    if(!vis[v]){
                        vis[v]=1;
                        q.push(v);
                    }
                }
            }
        }
        void work(){
            bfs();
            stack<int> s;
            s.push(1);
            while(!s.empty()){
                int u=s.top();s.pop();
                dep[u]=dep[fa[u][0]]+1;
                ans[u]=ans[fa[u][0]]+(u<=n);
                for(int i=1;i<=30;i++)
                    fa[u][i]=fa[fa[u][i-1]][i-1];
                for(int i=head[u];i;i=E[i].next){
                    int v=E[i].v;
                    if(!fa[v][0])fa[v][0]=u;
                    else fa[v][0]=lca(fa[v][0],u);
                    if(!(--cnt[v]))s.push(v);
                }
            }
            for(int i=1;i<=n;i++)
                if(dep[i])printf("%d
    ",ans[i]);
                else printf("-1
    ");
        }
    }dominator_tree;
    void add_edge(int x,int ll,int rr,int L,int R,int las){
        if(!las)return;
        if(R<ll||L>rr)return;
        if(L<=ll&&rr<=R){dominator_tree.add(x,las+n);return;}
        int mid=(ll+rr)>>1;
        add_edge(x,ll,mid,L,R,t[las].l);
        add_edge(x,mid+1,rr,L,R,t[las].r);
    }
    int add_point(int ll,int rr,int pos,int las,int v){
        int tmp=++ind;
        t[tmp]=t[las];
        if(las)dominator_tree.add(tmp+n,las+n);
        dominator_tree.add(tmp+n,v);
        if(ll==rr)return tmp;
        int mid=(ll+rr)>>1;
        if(pos<=mid)t[tmp].l=add_point(ll,mid,pos,t[las].l,v);
        else t[tmp].r=add_point(mid+1,rr,pos,t[las].r,v);
        return tmp;
    }
    int main(){
    //  freopen("parade.in","r",stdin);
    //  freopen("parade.out","w",stdout);
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)scanf("%d%d",&l[i],&r[i]);
        for(int i=n;i;i--){
            add_edge(i,1,n,l[i],r[i],rt[i+1]);
            rt[i]=add_point(1,n,a[i],rt[i+1],i);
        }
        dominator_tree.work();
        return 0;
    }
  • 相关阅读:
    解决Windows 10每次重启默认浏览器都被重置为IE的一个办法
    使用cookie登录百度网盘账号
    【C++ Primer | 14】重载运算
    Visual Studio Code 快捷键的使用
    【C++ Primer | 8】IO库
    Git push常见用法
    Git 基础
    Git连接GitHub仓库详解
    ceph关于rpm包构建的教程
    monitor综合
  • 原文地址:https://www.cnblogs.com/dream-maker-yk/p/9676354.html
Copyright © 2011-2022 走看看