Toposort
Accepts: 30
Submissions: 98
Time Limit: 10000/5000 MS (Java/Others)
Memory Limit: 131072/131072 K (Java/Others)
问题描述
给出n个点m条边的有向无环图. 要求删掉恰好k条边使得字典序最小的拓扑序列尽可能小.
输入描述
输入包含多组数据. 第一行有一个整数T, 表示测试数据组数. 对于每组数据: 第一行包含3个整数n, m和k (1≤n≤100000,0≤k≤m≤200000), 表示图中结点数目, 图中边的数目以及要删的边数. 接下来m行, 每行包含两个整数ui and vi, 表示存在一条ui到vi的有向边 (1≤ui,vi≤n). 输入保证给定的图是一个DAG. 输入数据中n的和不超过106. 输入数据中m的和不超过2⋅106.
输出描述
对于每组数据, 输出一个整数S=(i=1∑ni⋅pi) mod (109+7), 其中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
解析:
删掉恰好k条边使得字典序最小的拓扑序列尽可能小,使用优先队列维护编号最小点,存放度数小于等于剩下的可删的边的数量的点,注意判断点是否合法。
#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;
}