观察顺序

Description

有一个编号为1…N1≤N≤10^5)的排列,小明对数列进行了M次观察(1≤M≤50,000)。每个观察结果都是某些数的一个有序序列,表示这些数出现的顺序。比方说,如果小明的一次观察结果是序列251,则25的前面,51的前面。小明的观察结果是按优先级排列的,所以他的目标是最大化X的值,使得最终完整排列顺序能够符合前X个观察结果描述的状态。当多种排列顺序都能符合前X个状态时,小明会让编号较小的数放前面。请帮助小明求出该排列。

Input

第一行包含NM
接下来的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 34 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;
}

猜你喜欢

转载自blog.csdn.net/DancingZ/article/details/81487336