每日一题 2022-5-4

题目大意:就是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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值