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 = (\displaystyle\sum_{i=1}^{n}{i\cdot p_i}) \text{ 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\n",A);
    }
  return 0;
}

 

posted @ 2016-03-05 21:34  meekyan  阅读(572)  评论(2编辑  收藏  举报