现在国王想给城市重新编号,令第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的排列。
5 4 4 1 1 3 5 3 2 5
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的点一定在最后,那么根据序号大小从大到小编号,保证小编号一定留给序号小的且尽量靠前的点。
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.