zoukankan      html  css  js  c++  java
  • HDU5638 / BestCoder Round #74 (div.1) 1003 Toposort 线段树+拓扑排序

    Toposort


     
    问题描述
    给出nn个点mm条边的有向无环图. 要求删掉恰好kk条边使得字典序最小的拓扑序列尽可能小.
    
    输入描述
    输入包含多组数据. 第一行有一个整数TT, 表示测试数据组数. 对于每组数据:
    
    第一行包含3个整数nn, mm和kk (1 le n le 100000, 0 le k le m le 200000)(1n100000,0km200000), 表示图中结点数目, 图中边的数目以及要删的边数.
    
    接下来mm行, 每行包含两个整数u_iui​​ and v_ivi​​, 表示存在一条u_iui​​到v_ivi​​的有向边 (1 le u_i, v_i le n)(1ui​​,vi​​n).
    
    输入保证给定的图是一个DAG. 输入数据中nn的和不超过10^6106​​. 输入数据中mm的和不超过2 cdot 10^62106​​.
    输出描述
    对于每组数据, 输出一个整数S = (displaystylesum_{i=1}^{n}{icdot p_i}) 	ext{ mod } (10^9 + 7)S=(i=1n​​ipi​​) mod (109​​+7), 其中p_{1}, p_{2}, ..., p_{n}p1​​,p2​​,...,pn​​是字典序最小的那个拓扑序列.
    
    输入样例
    3
    4 2 0
    1 2
    1 3
    4 5 1
    2 1
    3 1
    4 1
    2 3
    2 4
    4 4 2
    1 2
    2 3
    3 4
    1 4
    输出样例
    30
    27
    30

     题解:

    参考下普通的用堆维护求字典序最小拓扑序, 用某种数据结构维护入度小于等于kk的所有点, 每次找出编号最小的, 并相应的减少kk即可.

    这个数据结构可以用线段树, 建立一个线段树每个节点[l,r][l,r]维护编号从ll到rr的所有节点的最小入度, 查询的时候只需要在线段树上二分, 找到最小的xx满足入度小于等于kk.

    复杂度O((n+m)log n)O((n+m)logn)

    ///1085422276
    
    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <algorithm>
    #include<vector>
    using namespace std;
    using namespace std ;
    typedef long long ll;
    #define mem(a) memset(a,0,sizeof(a))
    #define pb push_back
    const int  N=100000+50;
    const int  mod = 1e9+7;
    const int  inf = 1e9+7;
    
    int ind[N],head[N],t,n,m,K,vis[N];
    vector<int > ans;
    vector<int >G[N];
    struct ss {
       int l,r,sum,index;
    }tr[N*5];
    struct sss {
      int to,next;
    }e[N*20];
    void init() {
      t=1;mem(head);mem(ind);ans.clear();mem(vis);
      for(int i=0;i<N;i++)G[i].clear();
    }
    void add(int u,int v) {e[t].to=v;e[t].next=head[u];head[u]=t++;}
    void build(int k,int s,int t) {
         tr[k].l=s;tr[k].r=t;
         if(s==t) {
            tr[k].sum=ind[s];
            tr[k].index=s;
            return ;
         }
         int  mid=(s+t)>>1;
         build(k<<1,s,mid);
         build(k<<1|1,mid+1,t);
         tr[k].sum=min(tr[k<<1].sum,tr[k<<1|1].sum);
    }
    int ask(int k,int s,int t,int c) {
        int ret;
        if(tr[k].l==tr[k].r&&tr[k].l==s) {
                return tr[k].index;
        }
        int mid=(tr[k].l+tr[k].r)>>1;
        if(tr[k<<1].sum<=c) {
           ret=ask(k<<1,s,mid,c);
        }
        else  {
             ret=ask(k<<1|1,mid+1,t,c);
        }
        return ret;
    }
    void update(int k,int x,int c) {
         if(tr[k].l==tr[k].r&&tr[k].l==x) {
            tr[k].sum+=c;
            return ;
         }
         int mid=(tr[k].l+tr[k].r)>>1;
         if(x<=mid) update(k<<1,x,c);
         else update(k<<1|1,x,c);
         tr[k].sum=min(tr[k<<1].sum,tr[k<<1|1].sum);
    }
    int main() {
        int T;
        scanf("%d",&T);
        while(T--) {
            scanf("%d%d%d",&n,&m,&K);
            init();int u,v,check;
            for( int i=1;i<=m;i++) {
                scanf("%d%d",&u,&v);
                ind[v]++;
                G[u].pb(v);
            }
            build(1,1,n);
            for(int i=1;i<=n;i++) {
                check=ask(1,1,n,K);
                ans.pb(check);
                K-=ind[check];
                update(1,check,inf);
                for(int j=0;j<G[check].size();j++) {
                    update(1,G[check][j],-1);
                    ind[G[check][j]]--;
                }
            }
            ll A = 0;
            for(int i=0;i<ans.size();i++) {
                A=(A+1ll*(i+1)*ans[i])%mod;
            }
            printf("%I64d
    ",A);
        }
      return 0;
    }
  • 相关阅读:
    linux 常用命令大全
    socket的读写函数readn和writen函数
    python中exec 和eval的用法
    python中set集合介绍
    python中下划线变量的规则和意义
    关于小端字节序和大端字节序的解释
    需要学习的网站
    关于尾递归节省内存空间
    python中的with语句
    python中的类变量和对象变量,以及传值传引用的探究
  • 原文地址:https://www.cnblogs.com/zxhl/p/5245959.html
Copyright © 2011-2022 走看看