问给出的
N
N
N个
M
M
M的排列,按从头到尾依次加到序列首或尾的规则,共同能得到的新排列的个数,并给出字典序最小的方案。
询问有多组。
T
≤
50
,
N
,
M
≤
1000
,
∑
m
≤
5000
Tle50,N,Mle1000,sum mle5000
T≤50,N,M≤1000,∑m≤5000
题解
先加入队列的数位置不好确定,但最后加入的数一定只能再两端,不妨考虑从后往前推。
这样一来每个时刻已经构成的排列是一段前缀和一段后缀,记录指针
L
i
,
R
i
L_i,R_i
Li,Ri表示每个排序前端和后端添加到了新排列的哪个位置,令新排列为
A
A
A,注意对每个旧排列而言,最终得到的新排列
A
A
A的唯一的。
从后依次给每一个排列加入一个数
a
a
a(即从后往前一列一列地加),分四种情况:
1、若
A
[
L
i
]
A[L_i]
A[Li]和
A
[
R
i
]
A[R_i]
A[Ri]均为
0
0
0,则加入
L
i
L_i
Li并将
L
i
L_i
Li右移,答案乘
2
2
2;(因为此时加入右边也可以,但只考虑一半的情况)
2、若
A
[
L
i
]
=
a
A[L_i]=a
A[Li]=a或
A
[
R
i
]
=
a
A[R_i]=a
A[Ri]=a,则直接移动对应的指针;
3、若
A
[
L
i
]
=
0
A[L_i]=0
A[Li]=0或
A
[
R
i
]
=
0
A[R_i]=0
A[Ri]=0,则添加
a
a
a后移动对应的指针;
4、否则一定不合法,答案为
0
0
0。
这样便能得到方案数。
接着需要构造方案。
每一列数加完后,若此刻所有
L
i
L_i
Li相等且所有
R
i
R_i
Ri相等,即构成了一段可以翻转的区间,把它们记录下来,这种翻转是用来调整使字典序最小的。
最终可以得到一连串相互包含的这样的区间,从内往外枚举它们,时刻满足当前字典序最小,有两种情况:
1、若区间
i
i
i与区间
i
+
1
i+1
i+1右端点重合(左端点不可能重合,因为在上面的情况
1
1
1选择加入了左端点),当且仅当
A
[
p
[
i
+
1
]
.
l
]
<
A
[
p
[
i
]
.
l
]
A[p[i+1].l]<A[p[i].l]
A[p[i+1].l]<A[p[i].l]时,翻转区间
i
+
1
i+1
i+1,再翻转区间
i
i
i,即把
A
[
p
[
i
+
1
]
.
l
]
A[p[i+1].l]
A[p[i+1].l]调整到当前最左边;
2、否则若
i
i
i区间左端点大于右端点,则翻转区间
i
+
1
i+1
i+1,则翻转区间
i
i
i,因为要把
i
+
1
i+1
i+1更小的一侧专项右边,再随
i
i
i一起翻回来。
代码
#include<cstdio>#include<cstring>#include<algorithm>usingnamespace std;#define N 1010#define md 1000000007int a[N][N], A[N], L[N], R[N], st[N][2];intread(){int s =0;char x =getchar();while(x <'0'|| x >'9') x =getchar();while(x >='0'&& x <='9') s = s *10+ x -48, x =getchar();return s;}voidturn(int x){int s = st[x][0]+ st[x][1];for(int i = st[x][0]; i < s - i; i++)swap(A[i], A[s - i]);}intmain(){int tn =read();while(tn--){int n =read(), m =read(), i, j;memset(A,0,sizeof(A));for(i =1; i <= n; i++){for(j =1; j <= m; j++)scanf("%d",&a[i][j]);
L[i]=1, R[i]= m;}int tot =1, s =1;
st[1][0]=1, st[1][1]= m;for(j = m; j && s; j--){for(i =1; i <= n; i++){if(!A[L[i]]&&!A[R[i]]) s = s *((L[i]!= R[i])+1)% md, A[L[i]++]= a[i][j];elseif(A[L[i]]== a[i][j]) L[i]++;elseif(A[R[i]]== a[i][j]) R[i]--;elseif(A[L[i]]!= a[i][j]&& A[R[i]]==0) A[R[i]--]= a[i][j];elseif(A[R[i]]!= a[i][j]&& A[L[i]]==0) A[L[i]++]= a[i][j];else{
s =0;break;}}int ok =1;for(i =2; i <= n && ok; i++)if((L[i]!= L[i -1]|| R[i]!= R[i -1])) ok =0;if(ok) st[++tot][0]= L[1], st[tot][1]= R[1];}printf("%d
", s);if(s){for(j = tot; j; j--){if(j < tot && st[j][1]== st[j +1][1]){if(A[st[j][0]]> A[st[j +1][0]])turn(j +1),turn(j);}else{if(st[j][0]< st[j][1]&& A[st[j][0]]> A[st[j][1]]){if(j < tot)turn(j +1);turn(j);}}}for(i =1; i <= m; i++)printf("%d ", A[i]);puts("");}}return0;}