DZY Loves Topological Sorting
Accepts: 112
Submissions: 586
Time Limit: 4000/2000 MS (Java/Others)
Memory Limit: 131072/131072 K (Java/Others)
问题描述
一张有向图的拓扑序列是图中点的一个排列,满足对于图中的每条有向边 (u→v) 从 u 到 v ,都满足 u 在排列中出现在 v 之前。 现在,DZY有一张有向无环图(DAG)。你要在最多删去 k 条边之后,求出字典序最大的拓扑序列。
输入描述
输入有多组数据。 ( TestCase≤5 ) 第一行,三个正整数 n,m,k(1≤n,m≤105,0≤k≤m) . 接下来 m 行,每行两个正整数 u,v(u≠v,1≤u,v≤n) , 代表一条有向边 (u→v) .
输出描述
对于每组测试数据,输出一行字典序最大的拓扑序列。
输入样例
5 5 2 1 2 4 5 2 4 3 4 2 3 3 2 0 1 2 1 3
输出样例
5 3 1 2 4 1 3 2
Hint
数据1. 删除(2->3),(4->5)两条边,可以得到字典序最大的拓扑序列:(5,3,1,2,4).
官方题解:
因为我们要求最后的拓扑序列字典序最大,所以一定要贪心地将标号越大的点越早入队。我们定义点
i
的入度为
di
。假设当前还能删去
k
条边,那么我们一定会把当前还没入队的
di≤k
的最大的
i
找出来,把它的
di
条入边都删掉,然后加入拓扑序列。可以证明,这一定是最优的。
具体实现可以用线段树维护每个位置的
di
,在线段树上二分可以找到当前还没入队的
di≤k
的最大的
i
。于是时间复杂度就是
O((n+m)logn)
.
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
const int maxn=100010;
const int INF=1000000000;
int N,M,K;
int head[maxn],tot;
struct node
{
int v,next;
}edge[maxn];
int in[maxn];
void init()
{
memset(head,-1,sizeof(head));
memset(in,0,sizeof(in));
tot=0;
}
struct IntervalTree
{
int minv[maxn<<2];
void build(int o,int l,int r)
{
minv[o]=0;
if(l==r)
{
minv[o]=in[l];
return;
}
int mid=(l+r)>>1;
build(o<<1,l,mid);
build(o<<1|1,mid+1,r);
pushup(o);
}
void pushup(int o)
{
minv[o]=min(minv[o<<1],minv[o<<1|1]);
}
void update(int o,int l,int r,int id,int val)
{
if(l==r)
{
minv[o]=val;
return ;
}
int mid=(l+r)>>1;
if(id<=mid)update(o<<1,l,mid,id,val);
else update(o<<1|1,mid+1,r,id,val);
pushup(o);
}
int query(int o,int l,int r,int k)
{
if(minv[o]>k)return -1;
if(l==r)return l;
int mid=(l+r)>>1;
if(minv[o<<1|1]<=k)return query(o<<1|1,mid+1,r,k);
else return query(o<<1,l,mid,k);
}
}tree;
void add_edge(int u,int v)
{
edge[tot].v=v;
edge[tot].next=head[u];
head[u]=tot++;
}
int main()
{
while(scanf("%d%d%d",&N,&M,&K)!=EOF)
{
init();
for(int i=1;i<=M;i++)
{
int u,v;
scanf("%d%d",&u,&v);
add_edge(u,v);
in[v]++;
}
tree.build(1,1,N);
for(int i=1;i<=N;i++)
{
int id=tree.query(1,1,N,K);
K-=in[id];
in[id]=INF;
tree.update(1,1,N,id,INF);
for(int i=head[id];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
in[v]--;
tree.update(1,1,N,v,in[v]);
}
printf("%d",id);
if(i<N)printf(" ");
else printf("\n");
}
}
return 0;
}