本题是今天哈理工的一个网络赛上的排列组合题。n个人排成一队,队头的人编号为1,后面的人编号分别为2,3,…,n. 1号人前面没有人,i号人的前面是i-1。 现在Kim想让这n个人重新排队,要求重新排队后编号为i的人前面不能是i-1。问有多少种排队的方法。方案数可能很大,输出答案模1e9+7。
当时做这道题的时候有点乱,光想着打表找规律了,再者是才复出一个月,思维远不如以前,结果就是用了好多时间到最后还是没做出来。网络赛结束后静下心来想了一会儿还是想出来了。如果这道题让高三时的我做,应该很快能AC。
说说思路:用f(n)表示有n个人时的排列方法,现在考虑n+1的情况,当把1排在最后一位时,前面的数字相对于1的位置是任意的,也就是说前面的2~n+1有f(n)种排列方法;当末尾的数字是2~n时,前面的排列方法也是f(n)种,具体为什么我也不知道该怎么表达,拿2在末尾来举例,因为前n个数里没有了1,2组合,1和3是可以互换位置的,这样下来排列方式就会多,但是注意,这个时候以1结尾的数列是不能接在2前面的,于是又要少几种排列,刚好抵消了;当末尾数字是n+1时,前面的排列方法有f(n)-(f(n-1)-(f(n-2)-…)),还是拿具体数字举例,当n=5时,5在末尾时,前面的排列方法应该是f(4)-(4在末尾时的排列方法),而(4在末尾时的排列方法)又等于f(3)-(3在末尾时的排列方法)……递推下去。
递推公式为:f(1)=0,f(2)=1,f(3)=f(2)+f(2)+f(2)-0,f(4)=f(3)+f(3)+f(3)+f(3)-(f(2)-0),f(5)=f(4)+f(4)+f(4)+f(4)+(f(4)-(f(3)-(f(2)-0)))……
因为比赛已经结束,所以不能评测了,贴一个没有MOD的代码,应该是对的吧。。。。。。
-
#include<cstdio>
-
#include<cstring>
-
#include<cmath>
-
#include<algorithm>
-
using
namespace
std;
-
-
const
int MOD =
1000000007;
-
const
int maxn =
1000000+
10000;
-
-
int T;
-
int f[maxn];
-
-
void init()
-
{
-
freopen(
“in.txt”,
“r”,
stdin);
-
freopen(
“out.txt”,
“w”,
stdout);
-
}
-
-
void readdata()
-
{
-
scanf(
“%d”, &T);
-
}
-
-
void work()
-
{
-
f[
1] =
1; f[
2] =
1;
-
int cnt =
0;
-
for(
int i =
3; i <=
1000000; i++)
-
{
-
f[i] = f[i
-1]*i - cnt;
-
cnt = f[i
-1]-cnt;
-
}
-
while(T–)
-
{
-
int n;
-
scanf(
“%d”, &n);
-
printf(
“%d\n”,f[n]);
-
}
-
}
-
-
int main()
-
{
-
init();
-
readdata();
-
work();
-
return
0;
-
}