Description
有一个编号为1…N(1≤N≤10^5)的排列,小明对数列进行了M次观察(1≤M≤50,000)。每个观察结果都是某些数的一个有序序列,表示这些数出现的顺序。比方说,如果小明的一次观察结果是序列2、5、1,则2在5的前面,5在1的前面。小明的观察结果是按优先级排列的,所以他的目标是最大化X的值,使得最终完整排列顺序能够符合前X个观察结果描述的状态。当多种排列顺序都能符合前X个状态时,小明会让编号较小的数放前面。请帮助小明求出该排列。
Input
第一行包含N和M。
接下来的M行,每行描述了一个观察结果。
第i+1行描述了观察结果i,第一个数是观察结果中的数的数量mi,后面是一列mi个整数,给出这次观察中的顺序。
所有mi的和至多为200,000
Output
输出N个空格分隔的整数,给出一个1…N的排列,为小明求出的该排列。
Sample Input
4 3
3 1 2 3
2 4 2
3 3 4 1
Sample Output
1 4 2 3
解释:这里,小明有四个数,3个观察结果。前2个观察结果可以同时被满足,但不能满足所有的规则,这意味着总共有两种可能的顺序:1 4 2 3和4 1 2 3,第一种是字典序较小的。
二分验证+拓扑排序,一道不错的练手题。
#include<bits/stdc++.h>
using namespace std;
#define Inc(i,L,r) for(register int i=(L);i<=(r);++i)
const int N = 1e5+10;
vector<int>G[N],vec[N*5];
int n,m,d[N];
bool vst[N],inq[N];
inline void init(){
scanf("%d%d",&n,&m);
Inc(i,1,m){
int x,num;scanf("%d",&num);
Inc(j,1,num)scanf("%d",&x),vec[i].push_back(x);
}
}
inline bool dfs(int x){
if(inq[x])return inq[x]=0;
vst[x]=inq[x]=1;
int size=G[x].size();
Inc(i,0,size-1)if(!dfs(G[x][i]))return inq[x]=0;
return inq[x]=0,1;
}
inline bool check(int idx){
memset(vst,0,sizeof(vst));
Inc(i,1,n)G[i].clear();
Inc(i,1,idx){
Inc(j,1,vec[i].size()-1){
G[vec[i][j-1]].push_back(vec[i][j]);
}
}
Inc(i,1,n)if(!vst[i])if(!dfs(i))return 0;
return 1;
}
priority_queue<int,vector<int>,greater<int> >Q;
inline void topsort(int idx){
Inc(i,1,n)G[i].clear();
Inc(i,1,idx){
Inc(j,1,vec[i].size()-1){
G[vec[i][j-1]].push_back(vec[i][j]);
++d[vec[i][j]];
}
}
Inc(i,1,n)if(!d[i])Q.push(i);
while(!Q.empty()){
int x=Q.top();Q.pop();
cout<<x<<" ";
int size=G[x].size();
Inc(i,0,size-1){
if(!--d[G[x][i]])Q.push(G[x][i]);
}
}
}
inline void solv(){
int L=1,r=m,can;
while(L<=r){
int Mid=L+r>>1;
if(check(Mid))L=Mid+1,can=Mid;
else r=Mid-1;
}
topsort(can);
}
int main(){
// freopen("order.in","r",stdin);
// freopen("order.out","w",stdout);
init();
solv();
return 0;
}