题意: 定义合法的排列为:以 m 为第一个数的波浪型排列 ( 波浪型就是 (2 3 1 4) 这样的 增减增... 的排列方式 )
给定 n,m 求1---n全排列中 m 开头的合法排列
题解:
对于一个固定的 n
他这个排列的第一个数字他可能存在 1.从他开始递增
2.从他开始递减
那么我们需要2个数组来表示这 2 种状态 --> 1.递增数组 f[]
2.递减数组 g[]
假设排列是 : i j .......... (i 是第一个数字 , j 是第二个数字)
那么 f[i] 表示 i为第一个数,且 j > i (递增) 的合理排列数
那么 g[i] 表示 i为第一个数,且 j < i (递减) 的合理排列数
更新的方法就是对于 f[i] ,找到所有 j 开头的递减数组 g[j], (这里的 j 附属的 n` == n - 1 , 是上一级的 n)
对于 g[i],找到所有 j 开头的递增数组 f[j]
知道方法更新 一个 n ,那么从 1 递推下去更新到 n ,最后对与 m 分情况讨论一下就好了
代码: (听说要用无符号)
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define ull unsigned long long
int n,m;
ull f[52][52],g[52][52];
ull dp() {
ull res = 0;
if(m > 1) {
rep(i,1,m-1) {
res += f[n-1][i];
}
} else {
if(n <= 2) res = 1;
else res = g[n-1][2];
}
return res;
}
void init() {
f[1][1] = g[1][1] = 1;
rep(n,2,50) {
rep(i,1,n) {
rep(j,i,n-1) f[n][i] += g[n-1][j];
rep(j,1,i-1) g[n][i] += f[n-1][j];
}
}
}
int main() {
init();
int T;scanf("%d",&T);
rep(t,1,T) {
scanf("%d %d",&n,&m);
printf("Case %d: %llu\n",t,dp());
}
}