BestCoder Round #74 HDU 5638 Topsort

Toposort

 
 Accepts: 30
 
 Submissions: 98
 Time Limit: 10000/5000 MS (Java/Others)
 
 Memory Limit: 131072/131072 K (Java/Others)
问题描述
给出nn个点mm条边的有向无环图. 要求删掉恰好kk条边使得字典序最小的拓扑序列尽可能小.
输入描述
输入包含多组数据. 第一行有一个整数TT, 表示测试数据组数. 对于每组数据:

第一行包含3个整数nn, mmkk (1 \le n \le 100000, 0 \le k \le m \le 200000)(1n100000,0km200000), 表示图中结点数目, 图中边的数目以及要删的边数.

接下来mm行, 每行包含两个整数u_iui and v_ivi, 表示存在一条u_iuiv_ivi的有向边 (1 \le u_i, v_i \le n)(1ui,vin).

输入保证给定的图是一个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=1nipi) 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条边使得字典序最小的拓扑序列尽可能小,使用优先队列维护编号最小点,存放度数小于等于剩下的可删的边的数量的点,注意判断点是否合法。
#include<cstdio>
#include<vector>
#include<stack>
#include<cstring>
#include<cstdlib>
#include<vector>
#include <iostream>
#include<queue>
using namespace std;
const int N =200008, M=300008,INF=1000000000;
struct Node{
	int to,next;
}edge[M];
int head[N],tot,n,m,k,indeg[N];
bool vis[N];
long long ans,cnt;
void init(){
	memset(head, -1, sizeof(head));
	memset(indeg, 0, sizeof(indeg));
	tot = 0;
}
void add(int u, int to){
	indeg[to]++;
	edge[tot].to=to;
	edge[tot].next=head[u];
	head[u]=tot++;
}
void Topsort(){
    memset(vis,0,sizeof(vis));//此点在队列中或已排好序
    priority_queue< int, vector<int>, greater<int> > q;//最小堆
	for(int i=0;i<n;i++){
        if(indeg[i]<=k){
        	q.push(i);
        	vis[i]=true;
		}
	}
	while(!q.empty()){
        int cur=q.top();
        q.pop();
        if(indeg[cur]>k){//k是变化的
            vis[cur]=false;
            continue;
        }
        k-=indeg[cur];//删边
        indeg[cur]=0;
		ans=(ans+(++cnt)*(cur+1))%1000000007;
		for(int i = head[cur]; i != -1; i = edge[i].next){
			int to= edge[i].to;
			if(indeg[to]>0){
                indeg[to]--;
                if(indeg[to]<=k&&vis[to]==false){//防止多次入优先队列
                    q.push(to);
                    vis[to]=true;
                }
			}
		}
	}
}
int main(){
    int t;
    cin>>t;
    while(t--){
        scanf("%d %d %d",&n,&m,&k);
		init();
		int u, v;
		for(int i=0;i<m;i++){
			scanf("%d %d",&u,&v);
			add(u-1, v-1);
		}
		ans=cnt=0;
		Topsort();
		printf("%I64d\n",ans);
    }
	return 0;
}



                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值