vijos P1790拓扑编号

来源:互联网 发布:淘宝原创男装店铺 编辑:程序博客网 时间:2024/04/29 07:42

描述

H国有n个城市,城市与城市之间有m条单向道路,满足任何城市不能通过某条路径回到自己。

现在国王想给城市重新编号,令第i个城市的新的编号为a[i],满足所有城市的新的编号都互不相同,并且编号为[1,n]之间的整数。国王认为一个编号方案是优美的当且仅当对于任意的两个城市i,j,如果i能够到达j,那么a[i]应当<a[j]。

优美的编号方案有很多种,国王希望使1号城市的编号尽可能小,在此前提下,使得2号城市的编号尽可能小...依此类推。

格式

输入格式

第一行读入n,m,表示n个城市,m条有向路径。

接下来读入m行,每行两个整数:x,y
表示第x个城市到第y个城市有一条有向路径。

输出格式

输出一行:n个整数
第i个整数表示第i个城市的新编号a[i],输出应保证是一个关于1到n的排列。

样例1

样例输入1[复制]

5 44 11 35 32 5

样例输出1[复制]

2 3 5 1 4

限制

每个测试点1s

提示

30%的测试点满足:n <= 10, m <= 10
70%的测试点满足:n <= 1000, m <= 10000
100%的测试点满足:n <= 100000, m <= 200000
输入数据可能有重边,可能不连通,但保证是有向无环图。

来源

Topcode

分析:拓扑排序+堆优化

从前向后找每次找到序号最小的入度为0的点?样例就不对。(脑补一下为什么。。。)

从后向前反向贪心?可行。每次找出度为0的编号最大的点,删反向边,减出度,并令这个点的编号当前最大。

一个有向无环图中出度为0的点一定在最后,那么根据序号大小从大到小编号,保证小编号一定留给序号小的且尽量靠前的点。


Code:


program t;var n,m,i,x,y,l,j,p:longint;    head,c,ans:array[1..100000]of longint;    edge:array[1..200000,1..2]of longint;begin read(n,m); for i:=1 to n do head[i]:=-1; for i:=1 to m do  begin   read(x,y);   l:=l+1;   edge[l,1]:=x;   edge[l,2]:=head[y];   head[y]:=l;   c[x]:=c[x]+1;  end; for i:=n downto 1 do  begin   j:=n;   while (j>0)and(c[j]<>0) do j:=j-1;   ans[j]:=i;   c[j]:=n+1;   p:=head[j];   while p<>-1 do    begin     c[edge[p,1]]:=c[edge[p,1]]-1;     p:=edge[p,2];    end;  end; for i:=1 to n do write(ans[i],' ');end.

但这题的数据范围是100000拓扑排序复杂度O(n^2)

在每次找出度为0的点时用堆优化,复杂度O(nlogn)

Code:

program tt;var n,m,i,x,y,l,j,p:longint;    head,c,ans,heap:array[1..100000]of longint;    edge:array[1..200000,1..2]of longint;procedure add(x:longint);var i,j,t:longint;begin heap[l]:=x; i:=l; while i>1 do   begin   j:=i shr 1;   if heap[j]<heap[i] then     begin t:=heap[j];heap[j]:=heap[i];heap[i]:=t; i:=j;end   else    break;  end;end;procedure updata;var i,j,t:longint;begin i:=1; while i shl 1<=l do  begin   j:=i shl 1;   if (j<l)and(heap[j]<heap[j+1]) then j:=j+1;   if heap[i]<heap[j] then     begin t:=heap[i];heap[i]:=heap[j];heap[j]:=t; i:=j;end   else    break;  end;end;begin read(n,m); for i:=1 to n do head[i]:=-1; for i:=1 to m do  begin   read(x,y);   l:=l+1;   edge[l,1]:=x;   edge[l,2]:=head[y];   head[y]:=l;   c[x]:=c[x]+1;  end; for i:=1 to n do   if c[i]=0 then    begin l:=l+1;add(i);   end; for i:=n downto 1 do  begin   j:=heap[1];//每次加入度为0的点,用大根堆维护编号   ans[j]:=i;   c[j]:=n+1;   heap[1]:=heap[l];l:=l-1;updata;   p:=head[j];   while p<>-1 do    begin     c[edge[p,1]]:=c[edge[p,1]]-1; if c[edge[p,1]]=0 then begin l:=l+1;add(edge[p,1]);end;     p:=edge[p,2];    end;  end; for i:=1 to n do write(ans[i],' ');end.


0 0
原创粉丝点击