题目大意:你有一个初始为
1
到
问题一:恰好进行
k
次相邻交换,最后有多少不同的排列
问题二:进行不多于
(n,k<=3000)
思路:这道题没有什么新意,其主要思路就是将恰好变为最少,即一个排列我们要用最少的操作次数得到。
对于第一问,考虑一个排列,我们要求出在最少交换次数的情况下如何得到它。
设
f[i][j]
为到第
i
个数,目前交换了
考虑交换相邻的数会导致总的逆序对的奇偶性发生反转,假设一个排列用
i
次得到,那么如果
否则一定不可以得到(根据逆序对的奇偶性)
这样我们解决了第一问
第二问和第一问并没有什么区别,考虑这个可以两两交换,我们要在次数最少的情况下得到最终排列,从置换群的角度看最终的排列,构建有向图,发现最终排列的本质是一个个有向圈,很明显可以证明一个排列经过最少交换次数得到一定是从小到大插入有向圈里
根据这个性质我们直接设
f[i][j]
为前
i
个数使用了
代码:
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#define N 3000
using namespace std;
typedef long long LL;
LL ans,n,k,P,f[N + 5][N + 5];
int main(){
cin>>n>>k;
P = 1000000007LL;
memset(f,0,sizeof(f));
f[0][0] = 1;
for (int i = 0;i < n; ++i){
if (i) for (int j = 1;j <= k; ++j){
f[i][j] = f[i][j - 1] + f[i][j];
if (f[i][j] >= P) f[i][j] -= P;}
for (int j = 0;j <= k; ++j){
f[i + 1][j] = f[i + 1][j] + f[i][j];
if (f[i + 1][j] >= P) f[i + 1][j] -= P;
if (i + j + 1 <= N) f[i + 1][j + i + 1] = f[i + 1][j + i + 1] - f[i][j];
if (i + j + 1 <= N)if (f[i + 1][j + i + 1] < 0) f[i + 1][j + i + 1] += P;}
}
for (int i = 1;i <= k; ++i) f[n][i] = (f[n][i] + f[n][i - 1]) % P;
for (int i = 0;i <= k; ++i)
if (!((k - i)&1))
ans = (ans + f[n][i]) % P;
if (ans < 0) ans += P;
cout<<ans<<" ";
memset(f,0,sizeof(f));
f[0][0] = 1;
for (int i = 1;i <= n; ++i){
f[i][0] = f[i - 1][0];
for (int j = 1;j <= k; ++j)
f[i][j] = (f[i - 1][j] + f[i - 1][j - 1] * (i - 1) % P) % P;}
ans = 0;
for (int i = 0;i <= k; ++i) ans = (ans + f[n][i]) % P;
cout<<ans;
return 0;
}
总结:1.当求一些操作序列多少次的时候可以转化为在最少情况下得到,根据这个设计dp状态