题目大意:就是1~n的全排列,然后按照每一个数与左边最近的大于他的数连一个无向边,与右边最近的大于他的数也建立一个无向边,然后求出这1~n的全排列中有多少种可以按照题目的要求建成一个有环的图。
写本题目的时候,首先题目的意思我是一时半会没有理解完的,后来仔细想一想也就很简单了,就是排列中的每一个数找一个左边离他最近的比他他的数连一个边,右边也找一个比他大的并且最近的数连一个边,然后按照这个要求构成一个图。
知道题目意思之后,该怎么想呢。
1:n个数的全排列的个数,这个很简单就是 n! 。
2:怎么样构图才会形成一个环,这点需要我们知道在一个连通图中如果没有存在环的话,那边有且只有n-1条,也就是说当边的数量大于n-1的时候就会出现环。
3:以上都是我们还没有代入题目的时候该知道的东西,然后我们来看题目。我们将我们预先要知道的代入本题,题目要求的是带环的图的个数,俩种方法,1:直接求值。2:全排列的个数减去不存在环的个数。
4:我们先来看直接求值,很明显直接求值在本题不太实用,只要边大于等于n-1就会出现环,这样的情况很多,所以比较之下,第二种就n-1条边显得更可控制,所以我们选择第二种方法。
5:不存在环就是n-1条边,也就是说刚好每一个点都只会引出一条边,我们继续看题目的意思,每一个点引出一条边话也就是说,只有左边或者只有右边有数大于这个数,这样的话就只会有一条边。 比如说 现在有一个排列 x1 x2 x x3 x4 ,我们现在只考虑x这一个数,假如只要他连接在一条边上的话 也就是只能左边有x1 > x 或者x2>x , 或者只能是右边x3 > x 或者 x4 >x。 然后我们将这个规律普及到每一个数,我们不难得出我们的全排列必须得是一个单峰的数列。
6:单峰数列就是说 数列只能是增减 , 或者一直增的数列。
7 : 所以现在我们的题目就很简单了,第一个就是全排列的个数,这一点我们已经知道,然后我们在求出单峰数列的个数就行了。
8:单峰数列怎么求呢,我们可以这么来看,首先这些数是1~n,我们将每次最大的数放入数列中建成一个新的数列,(因为这样我们保证了之前原本数列里的数都大于这个数),就好像1~n,我们将n放在这里面,然后放n-1这样的话 n-1就可以放在左边或者右边的任意一边,因为n一定大于n-1。同理其他的数也是相同的,所以除了n之外,剩下的n-1个数都只要考虑放在这个新数列的左边或者右边就行,所以个数就是 2^(n-1)。
好了题目都说完了,然后就是代码的实现。
#include<iostream>
using namespace std;
typedef long long LL;
LL t[1000005];
const int mod =1e9+7;
LL pow(LL a , LL b)
{
LL ans = 1;
while(b)
{
if(b & 1) ans = (ans * a) % mod;
b >>= 1;
a = a * a % mod;
}
return ans;
}
int main()
{
int n;
int T;
cin >>T;
t[2] = 2;
for(int i = 3 ; i <= 1000004 ; i ++)
t[i] = (i * t[i - 1 ]) % mod;
while(T--)
{
LL x;
scanf("%lld",&x);
printf("%lld\n",(t[x] - pow(2 , x - 1) + mod ) %mod); // 如果t[x] 取模后减去pwo(2,x-1)小于0的话我们需要先加上mod再模才是正解
}
return 0;
}