1. 程式人生 > >BZOJ5280: [Usaco2018 Open]Milking Order(二分+拓撲)

BZOJ5280: [Usaco2018 Open]Milking Order(二分+拓撲)

5280: [Usaco2018 Open]Milking Order

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 123  Solved: 62
[Submit][Status][Discuss]

Description

Farmer John的N頭奶牛(1≤N≤105),仍然編號為1…N,正好閒得發慌。因此,她們發展了一個與Farmer John每 天早上為她們擠牛奶的時候的排隊順序相關的複雜的社會階層。經過若干周的研究,Farmer John對他的奶牛的社 會結構總計進行了M次觀察(1≤M≤50,000)。每個觀察結果都是他的某些奶牛的一個有序序列,表示這些奶牛應 該以與她們在序列中出現的順序相同的順序進行擠奶。比方說,如果Farmer John的一次觀察結果是序列2、5、1, Farmer John應該在給奶牛5擠奶之前的某個時刻給奶牛2擠奶,在給奶牛1擠奶之前的某個時刻給奶牛5擠奶。Farme r John的觀察結果是按優先順序排列的,所以他的目標是最大化X的值,使得他的擠奶順序能夠符合前X個觀察結果描 述的狀態。當多種擠奶順序都能符合前X個狀態時,Farmer John相信一個長期以來的傳統——編號較小的奶牛的地 位高於編號較大的奶牛,所以他會最先給編號最小的奶牛擠奶。更加正式地說,如果有多個擠奶順序符合這些狀態 ,Farmer John會採用字典序最小的那一個。擠奶順序x的字典序比擠奶順序y要小,如果對於某個j,xi=yi對所有i <j成立,並且xj<yj(也就是說,這兩個擠奶順序到某個位置之前都是完全相同的,在這個位置上x比y要小)。請 幫助Farmer John求出為奶牛擠奶的最佳順序。

Input

第一行包含N和M。 接下來的M行,每行描述了一個觀察結果。 第i+1行描述了觀察結果i,第一個數是觀察結果中的奶牛數量mi,後面是一列mi個整數,給出這次觀察中奶牛的順序。 所有mi的和至多為200,000

Output

輸出N個空格分隔的整數,給出一個1…N的排列,為Farmer John給他的奶牛們擠奶應該採用的的順序。

Sample Input

4 3
3 1 2 3
2 4 2
3 3 4 1

Sample Output

1 4 2 3

這裡,Farmer John有四頭奶牛,他的擠奶順序應該是奶牛1在奶牛2之前、奶牛2在奶牛3之前(第一個觀察結果)
,奶牛4在奶牛2之前(第二個觀察結果),奶牛3在奶牛4之前、奶牛4在奶牛1之前(第三個觀察結果)。前兩個觀
察結果可以同時被滿足,但是Farmer John不能同時滿足所有的規則,因為這樣的話會要求奶牛1在奶牛3之前,同
時奶牛3在奶牛1之前。這意味著總共有兩種可能的擠奶順序:1 4 2 3和4 1 2 3,第一種是字典序較小的。

HINT

Source

Gold

 

 

思路:一眼題,由於不方便按照順序加邊,然後每次判斷是否有環。 由於是最大的字首邊,我們按邊數二分然後判斷,然後可以tarjan判斷環,但是也可以直接拓撲判斷同時得到答案,就懶得寫tarjan了,因為如果有環,環裡的點是拓撲不出來的,最後判斷如果沒有拓撲完所有的點,說明有環,否則更新答案。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pb push_back
using namespace
std; const int maxn=400010; vector<int>G[maxn]; int res[maxn],ans[maxn],N,M; int Laxt[maxn],Next[maxn],To[maxn],ind[maxn],cnt,tot; void add(int u,int v){ Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v; ind[v]++; } bool check(int Mid) { rep(i,1,N) Laxt[i]=0,ind[i]=0; priority_queue<int,vector<int>,greater<int> >q; cnt=0; tot=0; rep(i,1,Mid){ rep(j,1,G[i].size()-1) add(G[i][j-1],G[i][j]); } rep(i,1,N) if(!ind[i]) q.push(i); while(!q.empty()){ int u=q.top(); ans[++tot]=u; q.pop(); for(int i=Laxt[u];i;i=Next[i]){ int v=To[i]; ind[v]--; if(!ind[v]) q.push(v); } } return tot==N; } int main() { scanf("%d%d",&N,&M); rep(i,1,M){ int num,x; scanf("%d",&num); rep(j,1,num) scanf("%d",&x),G[i].pb(x); } int L=1,R=M,Mid; while(L<=R){ Mid=(L+R)>>1; if(check(Mid)) { L=Mid+1; rep(i,1,N) res[i]=ans[i]; } else R=Mid-1; } rep(i,1,N) printf("%d ",res[i]); return 0; }