CS Academy Round 75 Permutations NTT

题目

给一个正整数n和询问数q,每次询问给出两个数x和y,问有多少长度为n的排列P满足:
py=maxni=1pi p y = m a x i = 1 n p i
2px<py 2 p x < p y
n,q<10^5

分析

因为x只是一个位置,所以对于给定的y而言不同的x是不会对答案产生影响的。
所以我们只用考虑y就好了。
然后呢我们可以发现答案其实要我们求的就是这个式子:

ans(y)=i=1ni12Cy2i2(y2)!(ny)! a n s ( y ) = ∑ i = 1 n ⌊ i − 1 2 ⌋ C i − 2 y − 2 ( y − 2 ) ! ( n − y ) !

然后我们就可以用ntt求解了。

代码

#include <bits/stdc++.h>

typedef long long ll;

const int N = 100010;
const int MOD = 998244353;

int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}

int f[N * 4],g[N * 4],rev[N * 4];

int ny[N],jc[N];

int L,n,q;

int ksm(int x,int y)
{
    int res = 1;
    while (y)
    {
        if (y & 1)
            res = 1ll * res * x % MOD;
        x = 1ll * x * x % MOD;
        y >>= 1;
    }
    return res;
}

void ntt(int *a, int f)
{
    for (int i = 0; i < L; i++)
        if (i < rev[i])
            std::swap(a[i], a[rev[i]]);
    for (int i = 1; i < L; i <<= 1)
    {
        int wn = ksm(3, f == 1 ? (MOD - 1) / i / 2 : MOD - 1 - (MOD - 1) / i / 2);
        for (int j = 0; j < L; j += (i << 1))
        {
            int w = 1;
            for (int k = 0; k < i; k++)
            {
                int u = a[j + k], v = 1ll * a[j + k + i] * w % MOD;
                a[j + k] = (u + v) % MOD;
                a[j + k + i] = (u + MOD - v) % MOD;
                w = 1ll * w * wn % MOD;
            }
        }
    }
    int nyL = ksm(L, MOD - 2);
    if (f == -1)
        for (int i = 0; i < L; i++)
            a[i] = 1ll * a[i] * nyL % MOD;
}

int main()
{
    n = read(), q = read();
    jc[0] = jc[1] = ny[0] = ny[1] = 1;
    for (int i = 2; i <= n; i++)
        jc[i] = 1ll * jc[i - 1] * i % MOD, ny[i] = 1ll * (MOD - MOD / i) * ny[MOD % i] % MOD;
    for (int i = 2; i <= n; i++)
        ny[i] = 1ll * ny[i - 1] * ny[i] % MOD;
    for (int i = 3; i <= n; i++)
        f[i] = 1ll * (i - 1) / 2 * jc[i - 2] % MOD;
    for (int i = 0; i <= n; i++)
        g[i] = ny[n - i];
    int lg = 0;
    for (L = 1; L <= n * 2; L <<= 1, lg++);
    for (int i = 0; i < L; i++)
        rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (lg - 1));
    ntt(f, 1), ntt(g, 1);
    for (int i = 0; i < L; i++)
        f[i] = 1ll * f[i] * g[i] % MOD;
    ntt(f, -1);
    while (q--)
    {
        int x = read(), y = read();
        printf("%d\n", 1ll * f[n + y] * jc[n - y] % MOD);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值