洛谷P8299 题解

题意

你需要构造一个 $1∼N$ 的排列,满足 $M$ 个要求,要求形式是给出 $l,r$ 中的最大值或最小值。

思路

不难看出,这是一个二分图匹配问题。我们要给每个位置匹配一个数。那么怎么建边呢?我们发现根据题目给出的要求,好像不是很好直接看出每个位置能匹配哪些数。不过正难则反,题目给出的要求是很容易看出每个位置不能匹配哪些数的。显然如果题目给出 $[l,r]$ 中最小的数是 $x$,那么这些位置不能与任何小于 $x$ 的数匹配。大于同理。

这样就够了吗?其实是有问题的。我们这样无法保证 $x$ 这个数一定被分配到 $[l,r]$ 这个区间里。所以我们还有让所有不在 $[l,r]$ 的数都不能匹配到 $x$,这样就保证满足题目条件了。

至于二分图匹配部分,可以用匈牙利,当然我用的 Dinic。

丑陋的代码(跑得贼慢)

#include <bits/stdc++.h>
#define int long long
using namespace std;
bool ok[205][205];
int n,m,cnt=1,lst[415],s,t,inf=0x3f3f3f3f,now[415],dep[415],flow,seat[415]; 
struct edge{
	int f,t,val,lst;
	edge(int f=0,int t=0,int val=0,int lst=0):
		f(f),t(t),val(val),lst(lst){}; 
}e[400005];
void add(int u,int v,int w){
	e[++cnt]=edge(u,v,w,lst[u]);lst[u]=cnt;
	e[++cnt]=edge(v,u,0,lst[v]);lst[v]=cnt;
}
bool bfs(){
	memset(dep,-1,sizeof(dep));
	dep[s]=0;
	queue<int> q;
	q.push(s);
	while(!q.empty()){
		int u=q.front();
		q.pop();
		now[u]=lst[u];
		for(int i=lst[u];i;i=e[i].lst){
			int v=e[i].t;
			if(dep[v]==-1&&e[i].val){
				dep[v]=dep[u]+1;
				q.push(v);
			}
		}
	} 
	return dep[t]!=-1;
}
int dfs(int u,int in){
	if(u==t)return in;
	int out=0;
	for(int i=now[u];i;i=e[i].lst){
		now[u]=i;
		int v=e[i].t;
		if(dep[v]==dep[u]+1&&e[i].val){
			int go=dfs(v,min(in,e[i].val));
			in-=go,out+=go;
			e[i].val-=go,e[i^1].val+=go;
			if(!in)break;
		}
	}
	if(!out)dep[u]=-1;
	return out;
}
signed main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int op,u,v,x;
		cin>>op>>u>>v>>x;
		if(op==1){
			for(int j=u;j<=v;j++){
				for(int k=x+1;k<=n;k++){
					ok[j][k]=1;
				}
			}
			for(int j=1;j<u;j++){
				ok[j][x]=1;
			}
			for(int j=v+1;j<=n;j++){
				ok[j][x]=1;
			}
		}
		else{
			for(int j=u;j<=v;j++){
				for(int k=1;k<x;k++){
					ok[j][k]=1;
				}
			}
			for(int j=1;j<u;j++){
				ok[j][x]=1;
			}
			for(int j=v+1;j<=n;j++){
				ok[j][x]=1;
			}
		}
	}
	s=0,t=n*2+1;
	for(int i=1;i<=n;i++){
		add(s,i,1);
		add(i+n,t,1);
		for(int j=1;j<=n;j++){
			if(!ok[i][j])add(i,j+n,1);
		}
	}
	while(bfs()){
		flow+=dfs(s,inf);
	}
	if(flow!=n)puts("-1");
	else{
		for(int i=2;i<=cnt;i++){
			if(!e[i].val&&e[i^1].val&&e[i].f<=n&&e[i].f&&e[i].t>n&&e[i].t!=t){
				seat[e[i].f]=e[i].t;
			}
		}
		for(int i=1;i<=n;i++)cout<<seat[i]-n<<' ';
	}
	return 0;
}