题目
题目地址: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 n≤1000,∑m≤5000
题解
这题不能用 D P DP DP 之类的算法来处理。
发现每个排列的第 m m m 个元素必定在所得队列的头或者尾处,因此考虑倒着求这个队列,这是一个从队列的两端向中间填数字的过程。
假设现在处理到第 k k k 个元素,第 i i i 个排列完成了队列的 1.. x i − 1 1..x_i-1 1..xi−1 及 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 个序列,分类讨论:
- 若 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 中的哪一个相等,把那一位给补上;
- 若 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 ;
- 若 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 ;
- 若 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 k−1 个元素即可;
否则,令这两个值为 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 k−1 个元素即可;
否则,这个数既可以放到 l l l 上,又可以放到 r r r 上,有两种方案。
那么怎么知道哪种方案字典序最小呢?直接往后暴力递归看,假设它放在 l l l 上,后面会不会有比它小的数满足放到 l + 1 l+1 l+1 上的条件就行了。
时间复杂度 O ( n ∑ m ) O(n\sum m) O(n∑m) 。
代码
#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;
}