PE 427

本文介绍了一种关于排列问题的算法实现,针对长度为n的整数排列集合,通过递推公式计算特定条件下的求和问题。利用组合数学的方法简化计算,并通过代码实现了O(NlogN)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:设 S 为长度为 n,元素大小为 1 到 n 的整数的排列集合,L(X) 为排列 X 中连续值相同的最长长度,求 X|SL(S),其中 n=7500000

F[n][k] 表示长度为 n 的序列,L < k 的方案数
易得递推式

F[n][k]=F[n1][k]nF[nk][k](n1)

第二维都是 k,所以可以枚举 k 来算

考虑这个转移,可以理解成 0 -> n,每次走 k 步或 1 步,即 n=ak+b,贡献分别为 n 、(1 - n)
用组合数可以表示成

a=0nk(a+naka)(1n)annak

答案就是 ni=1i(f[n][i+1]f[n][i])=nn+1ni=1f[n][i]

但注意一点,当 n == k 的时候,其实前面没有限制,所以答案会有 1 的差,所以要减掉 QAQ

求 n、(1 - n) 的次方可以预处理,不然复杂度还会加个 log …(好吧,只有我会写错QAQ

复杂度 O(NlogN)

【答案】97138867

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<string>
#include<cstring>
#include<ctime>
//#include<cmath>
#include<algorithm>
#define M 1000000009
#define N 7500000
#define LL long long
using namespace std;

LL n = N,ans;
LL frac[N + 5],inv[N + 5],pow[N + 5],pow1[N + 5];

LL ksm(LL a,LL b)
{
    LL ret = 1;
    for (;b;b >>= 1,a = a * a % M)
        if (b & 1) ret = ret * a % M;
    return ret;
}

LL C(int n,int m)
{
    if (!m || n == m) return 1;
    return frac[n] * inv[m] % M * inv[n - m] % M;
}

LL cal(int m,int k)
{
    LL ret = 0;
    for (int a = 0;a * k <= m;a ++)
        (ret += C(a + m - a * k,a) * pow1[a] % M * pow[m - a * k]) %= M;
    return ret;
}

int main()
{
    frac[0] = pow1[0] = pow[0] = 1;
    for (int i = 1;i <= n;i ++)
    {
        frac[i] = frac[i - 1] * i % M;
        pow[i] = pow[i - 1] * n % M;
        pow1[i] = pow1[i - 1] * (1ll - n) % M;
    }
    inv[n] = ksm(frac[n],M - 2);
    for (int i = n;i;i --) inv[i - 1] = inv[i] * i % M;
    for (int k = 2;k <= n;k ++)
        (ans += (cal(n - 1,k) - cal(n - k,k)) * n) %= M;
    ans = (ksm(n,n + 1) - ans) % M;
    if (ans < 0) ans += M;
    cout << ans << endl;

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值