gmoj 6652. 【2020.05.27省选模拟】序列 题解

题目

题目地址:https://gmoj.net/senior/#main/show/6652

题目大意:

给你 n n n 1.. m 1..m 1..m 的排列,对于每一个排列,你都可以从前往后将元素依次插入一个队列中(要么放到队列头,要么放在队列尾)。问有多少种队列是这 n n n 个排列都可以到达的,并要求给出这些队列中字典序最小的那一个。

n ≤ 1000 , ∑ m ≤ 5000 n\le 1000,\sum m\le 5000 n1000,m5000

题解

这题不能用 D P DP DP 之类的算法来处理。

发现每个排列的第 m m m 个元素必定在所得队列的头或者尾处,因此考虑倒着求这个队列,这是一个从队列的两端向中间填数字的过程。

假设现在处理到第 k k k 个元素,第 i i i 个排列完成了队列的 1.. x i − 1 1..x_i-1 1..xi1 y i + 1.. m y_i+1..m yi+1..m 的部分(即现在要在 x i x_i xi y i y_i yi 上填数字)。

l = max ⁡ x i , r = min ⁡ y i l=\max x_i,r=\min y_i l=maxxi,r=minyi

如下图:

在这里插入图片描述

由于要求队列是可以由每一行得到的, 1.. l 1..l 1..l r . . m r..m r..m 上的数字已经被固定了,令 s i s_i si 表示队列上第 i i i 位的数字。

对于第 i i i 个序列,分类讨论:

  1. x i < l , 且 y i > r x_i<l,\text{且} y_i>r xi<l,yi>r ,就看 a i , k a_{i,k} ai,k s x i , s y i s_{x_i},s_{y_i} sxi,syi 中的哪一个相等,把那一位给补上;
  2. x i = l , 且 y i > r x_i=l,\text{且} y_i>r xi=l,yi>r ,若 a i , k = s y i a_{i,k}=s_{y_i} ai,k=syi ,就把 y i y_i yi 补上,否则 a i , k a_{i,k} ai,k 只能放在 l l l 处,即 s l = a i , k s_l=a_{i,k} sl=ai,k
  3. x i < l , 且 y i = r x_i<l,\text{且} y_i=r xi<l,yi=r ,若 a i , k = s x i a_{i,k}=s_{x_i} ai,k=sxi ,就把 x i x_i xi 补上,否则 a i , k a_{i,k} ai,k 只能放在 r r r 处,即 s r = a i , k s_r=a_{i,k} sr=ai,k
  4. x i = l , 且 y i = r x_i=l,\text{且} y_i=r xi=l,yi=r a i , k a_{i,k} ai,k 既可以放在 l l l 处,又可以放在 r r r 处;

把情况 2 , 3 , 4 2,3,4 2,3,4 中可以放到 l , r l,r l,r 上的 a i , j a_{i,j} ai,j 都取出,若有 a i , j a_{i,j} ai,j 有多于2种值,就说明无解(最多只能把一个数放在 l l l ,再把一个数放在 r r r ,不能放第三个数)。因为不可能有所有数都不满足情况 2 , 3 , 4 2,3,4 2,3,4 的情况,现在就只可能有一种或两种 a i , j a_{i,j} ai,j 的值。

对于有两种值的情况,如果 s l , s r s_l,s_r sl,sr 中至少有一个已经被填了数字,继续处理第 k − 1 k-1 k1 个元素即可;

否则,令这两个值为 p , q p,q p,q ,可以发现既可以是 s l = p , s r = q s_l=p,s_r=q sl=p,sr=q ,也可以是 s l = p , s r = q s_l=p,s_r=q sl=p,sr=q ,因此有两种方案。因为我们在求方案的同时还要求字典序最小的队列,因此选择其中字典序较小的那个即可。

对于有一种值的情况,如果 s l , s r s_l,s_r sl,sr 中有一个已经被填了数字了,继续处理第 k − 1 k-1 k1 个元素即可;

否则,这个数既可以放到 l l l 上,又可以放到 r r r 上,有两种方案。

那么怎么知道哪种方案字典序最小呢?直接往后暴力递归看,假设它放在 l l l 上,后面会不会有比它小的数满足放到 l + 1 l+1 l+1 上的条件就行了。

时间复杂度 O ( n ∑ m ) O(n\sum m) O(nm)


代码

#include<cstdio>
using namespace std;
#define fo(i,l,r) for(i=l;i<=r;++i)
#define N 1005
const int P=1000000007;
char buf[100005],*l=buf,*r=buf;
inline char gc()
{
    
    return l==r&&(r=(l=buf)+fread(buf,1,100005,stdin),l==r)?EOF:*l++;}
inline void read(int &k)
{
    
    
	char ch;while(ch=gc(),ch<'0'||ch>'9');k=ch-48;
	while(ch=gc(),ch>='0'&&ch<='9') k=k*10+ch-48;
}
int a[N][N],num[N],x[N],y[N],n,m;
bool b[N],locked[N];
inline void swap(int &x,int &y){
    
    int z=x;x=y,y=z;}
inline int finish()
{
    
    
	int i,j,l,r;
//	puts("check");
//	fo(i,1,m) printf("%d\t",num[i]);
//	puts("");
	fo(i,1,n)
	{
    
    
		l=1,r=m;
		for(j=m;j;--j)
		{
    
    
			if(a[i][j]==num[l]) ++l;
			else if(a[i][j]==num[r]) --r;
			else return 0;
		}
	}
	return 1;
}
bool judge(int k,int num)
{
    
    
	if(!k) return 1;
	int i,p=0,q=0;
	fo(i,1,n) if(!locked[a[i][k]])
	{
    
    
		if(!p) p=a[i][k];
		else if(p!=a[i][k])
		{
    
    
			if(!q) q=a[i][k];
			else if(q!=a[i][k]) return 1;
		}
	}
	if(!p) return judge(k-1,num);
	if(!q)
	{
    
    
		if(p<num) return 0;
		locked[p]=1;
		bool res=judge(k-1,num);
		locked[p]=0;
		return res;
	}
	return num<p&&num<q;
}
int solve(int k,int l,int r)
{
    
    
	if(l>r||!k) return finish();
	int i,p=0,q=0;
//	printf("[%d,%d]\t%d\n",l,r,k);
//	fo(i,1,n) printf("(%d,%d)\t",x[i],y[i]);
//	puts("");
//	fo(i,1,m) printf("%d\t",num[i]);
//	puts("");
	fo(i,1,n)
	{
    
    
		b[i]=1;
		if(x[i]<l&&a[i][k]==num[x[i]]) ++x[i],b[i]=0;
		else if(y[i]>r&&a[i][k]==num[y[i]]) --y[i],b[i]=0;
		if(b[i]&&x[i]<l&&y[i]>r) return 0;
//		printf("%d\t",b[i]?a[i][k]:-1);
	}
//	puts("");
	fo(i,1,n) if(b[i]) break;
	if(i>n) return solve(k-1,l,r);
	fo(i,1,n) if(b[i])
	{
    
    
		if(!p) p=a[i][k];
		else if(a[i][k]!=p)
		{
    
    
			if(q&&q!=a[i][k]) return 0;
			q=a[i][k];
		}
	}
	if(p>q) swap(p,q);
	int cnt=0;
	bool b1,b2;
	if(!p)
	{
    
    
		fo(i,1,n) if(b[i]&&x[i]<l&&y[i]>l) break;
		if(i>n) ++cnt;b1=i>n;
		fo(i,1,n) if(b[i]&&y[i]>r&&x[i]<r) break;
		if(i>n) ++cnt;b2=i>n;
		if(!cnt) return 0;
		locked[q]=1;
		if(b1&&(judge(k-1,q)||!b2))
		{
    
    
			num[l]=q;
			fo(i,1,n) if(b[i]) x[i]==l?++x[i]:--y[i];
			return l==r?finish():1LL*cnt*solve(k-1,l+1,r)%P;
		}
		else
		{
    
    
			num[r]=q;
			fo(i,1,n) if(b[i]) y[i]==r?--y[i]:++x[i];
			return l==r?finish():1LL*cnt*solve(k-1,l,r-1)%P;
		}
	}
	fo(i,1,n) if(b[i])
	{
    
    
		if(a[i][k]==p&&x[i]<l) break;
		if(a[i][k]==q&&y[i]>r) break;
	}
	if(i>n) ++cnt;b1=i>n;
	swap(p,q);
	fo(i,1,n) if(b[i])
	{
    
    
		if(a[i][k]==p&&x[i]<l) break;
		if(a[i][k]==q&&y[i]>r) break;
	}
	if(i>n) ++cnt;b2=i>n;
	if(!cnt) return 0;
	if(!b1) swap(p,q);
	locked[p]=locked[q]=1;
	num[l]=q,num[r]=p;
	fo(i,1,n) if(b[i])
	{
    
    
		if(a[i][k]==q) ++x[i];
		else --y[i];
	}
	if(l==r) return finish();
	return 1LL*cnt*solve(k-1,l+1,r-1)%P;
}
int main()
{
    
    
	freopen("array.in","r",stdin);
	freopen("array.out","w",stdout);
	int T,i,j,ans;
	read(T);
	while(T--)
	{
    
    
		read(n),read(m);
		fo(i,1,n) fo(j,1,m)
			read(a[i][j]);
		fo(i,1,n) x[i]=1,y[i]=m;
		fo(i,1,m) num[i]=0,locked[i]=0;
		ans=solve(m,1,m);
		printf("%d\n",ans);
		if(ans)
		{
    
    
			fo(i,1,m-1) printf("%d ",num[i]);
			printf("%d\n",num[m]);
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/huangzihaoal/article/details/114272523