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
代码:
#pragma warning(disable:4996)
#include <iostream>
#include <functional>
#include <algorithm>
#include <cstring>
#include <vector>
#include <string>
#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <deque>
#include <set>
#include <map>
using namespace std;
typedef long long ll;
#define INF 0x333f3f3f
const ll mod = 1000000007;
const int maxn = 100005;
int n, m, k;
int vis[maxn], in[maxn];
vector<int>con[maxn];
priority_queue<int, vector<int>, greater<int>>qu;
void init()
{
int i, j, k;
while (!qu.empty())
qu.pop();
for (i = 1; i <= n; i++)
{
vis[i] = 0;
in[i] = 0;
qu.push(i);
con[i].clear();
}
}
void solve()
{
ll ans;
int i, j, k, nx, sz;
int u, v;
scanf("%d%d%d", &n, &m, &k);
init();
for (i = 1; i <= m; i++)
{
scanf("%d%d", &u, &v);
con[u].push_back(v);
in[v]++;
}
nx = 1;
ans = 0;
while (!qu.empty())
{
v = qu.top();
qu.pop();
if (vis[v])continue;
if (in[v] <= k)
{
vis[v] = 1;
k -= in[v];
ans = (ans + (ll)nx*(ll)v) % mod;//注意这块long long
nx++;
sz = con[v].size();
for (i = 0; i < sz; i++)
{
u = con[v][i];
if (vis[u] == 0)
{
qu.push(u);
in[u]--;
}
}
}
}
printf("%lld\n", ans);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("i.txt", "r", stdin);
freopen("o.txt", "w", stdout);
#endif
int t;
scanf("%d", &t);
while (t--)
{
solve();
}
//system("pause");
return 0;
}