链接:http://acm.hdu.edu.cn/showproblem.php?pid=5638
题意:给出n个点m条边的有向无环图. 要求删掉恰好k条边使得字典序最小的拓扑序列尽可能小.
分析:因为要字典序最小,所以每次要找入度小于等于k的最小节点,用线段树维护即可。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<queue>
#include<cmath>
#include<stack>
#include<set>
#include<map>
#define INF 0x3f3f3f3f
#define Mn 100010
#define Mm 200005
#define mod 1000000007
#define CLR(a,b) memset((a),(b),sizeof((a)))
#define CPY(a,b) memcpy ((a), (b), sizeof((a)))
#pragma comment(linker, "/STACK:102400000,102400000")
#define ul u<<1
#define ur (u<<1)|1
using namespace std;
typedef long long ll;
int read() {
char c=getchar();
int re=0,f=1;
while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9') {re=re*10+c-'0';c=getchar();}
return re;
}
struct edge {
int next,v;
}e[Mm];
int head[Mn],tot;
void addedge(int u,int v) {
e[tot].v=v;
e[tot].next=head[u];
head[u]=tot++;
}
void init() {
CLR(head,-1);
tot=0;
}
int minn[Mn*4];
int degree[Mn];
void push_up(int u) {
minn[u]=min(minn[ul],minn[ur]);
}
void build(int l,int r,int u) {
if(l==r) {
minn[u]=degree[l];
return ;
}
int mid=(l+r)>>1;
build(l,mid,ul);
build(mid+1,r,ur);
push_up(u);
}
void update(int pos,int l,int r,int u,int x) {
if(l==r) {
minn[u]+=x;
return ;
}
int mid=(l+r)>>1;
if(pos<=mid) update(pos,l,mid,ul,x);
else update(pos,mid+1,r,ur,x);
push_up(u);
}
int query(int l,int r,int u,int &k) {
if(l==r) {
k-=minn[u];
if(k<0) k=0;
return l;
}
int mid=(l+r)>>1;
if(k>=minn[ul]) return query(l,mid,ul,k);
else return query(mid+1,r,ur,k);
}
int main() {
int t=read();
while(t--) {
init();
CLR(degree,0);
int n=read(),m=read(),k=read();
for(int i=1;i<=m;i++) {
int u=read(),v=read();
addedge(u,v);
degree[v]++;
}
build(1,n,1);
ll sum=0;
for(int i=1;i<=n;i++) {
int u=query(1,n,1,k);
update(u,1,n,1,INF);
for(int j=head[u];~j;j=e[j].next) {
int v=e[j].v;
update(v,1,n,1,-1);
}
sum=(sum+((ll)i*u)%mod)%mod;
}
cout<<sum<<endl;
}
return 0;
}