P3200 [HNOI2009] 有趣的数列

哇,太恶心了

思路

首先我们将题意简化,简化后为对于任意一个偶数位所填数必然大于等于自己的下标,那么考虑填数,如果填的偶数比奇数多,那么此时要么填尽偶数后失败,或者下一个位置填奇数就炸,比如偶数刚好多一个,那么必然有一个偶数放在了奇数位,那么本来下一个要填的偶数往前移了一个,导致接下来只能继续填偶数,然后就失败了,所以我们的到结论,任何时候都要保证偶数个数比奇数个数少,所以可以将其转化为卡特兰数,然后一看数据 \(p\) 可为合数,直接寄

code

#include <iostream>

using namespace std;

const int MaxN = 2e6 + 10;

struct math {
  int qpow(int a, int b, int p) {
    int res = 1;
    for (int i = 1; i <= b; i <<= 1) {
      (b & i) && (res = 1ll * res * a % p);
      a = 1ll * a * a % p;
    }
    return res;
  }

  int exgcd(int a, int b, int &x, int &y) {
    if (!b) return x = 1, y = 0, a;
    int g = exgcd(b, a % b, y, x);
    return y = (y - (a / b * x)), g;
  }

  int inv(int x, int p) {
    int b, y;
    exgcd(x, p, b, y);
    return (b % p + p) % p;
  }

  int f(int x, int p, int mod) {
    if (!x) return 1;
    int res = 1;
    for (int i = 1; i <= mod; i++) {
      (i % p) && (res = 1ll * res * i % mod);
    }
    res = qpow(res, x / mod, mod);
    for (int i = 1; i <= x % mod; i++) {
      (i % p) && (res = 1ll * res * i % mod);
    }
    return 1ll * res * f(x / p, p, mod) % mod;
  }

  int CRT(int k, int a[], int r[]) {
    int n = 1, res = 0;
    for (int i = 1; i <= k; i++) n *= r[i];
    for (int i = 1; i <= k; i++) {
      int m = n / r[i];
      res = (res + 1ll * a[i] * m % n * inv(m, r[i]) % n) % n;
    }
    return res;
  }

  bool vis[MaxN];
  int p[MaxN], tot;
  void OLS() {
    for (int i = 2; i < MaxN; i++) {
      if (!vis[i]) p[++tot] = i;
      for (int j = 1; j <= tot && 1ll * p[j] * i < MaxN; j++) {
        vis[p[j] * i] = 1;
        if (i % p[j] == 0) break;
      }
    }
  }

  int get_ans(int n, int m, int p, int mod) {
    int cnt = 0;
    for (int i = n; i; i /= p) cnt += i / p;
    for (int i = m; i; i /= p) cnt -= i / p;
    for (int i = n - m; i; i /= p) cnt -= i / p;
    return 1ll * qpow(p, cnt, mod) * f(n, p, mod) % mod * inv(f(m, p, mod), mod) % mod * inv(f(n - m, p, mod), mod) % mod;
  }

  int a[MaxN], cp[MaxN], mp[MaxN], ff[MaxN], cnt;

  void breakdown(int mod) {
    cnt = 0;
    for (int i = 1; 1ll * p[i] * p[i] <= mod; i++) {
      if (mod % p[i] == 0) {
        for (mp[++cnt] = 1, cp[cnt] = p[i]; mod % p[i] == 0; mp[cnt] *= p[i], mod /= p[i]) {
        }
      }
    }
    (mod > 1) && (mp[++cnt] = mod, cp[cnt] = mod);
    if (cnt == 1) {
      for (int i = 1; i < MaxN; i++) {
        ff[i] = 1ll * ff[i - 1] * i % mod;
      }
    }
  }

  int exLucas(int n, int m, int mod) {
    if (n < m) return 0;
    if (cnt == 1) {  // 大质数去死吧!
      return 1ll * ff[n] * qpow(ff[m], mod - 2, mod) % mod * qpow(ff[n - m], mod - 2, mod) % mod;
    }
    for (int i = 1; i <= cnt; i++) {
      a[i] = get_ans(n, m, cp[i], mp[i]);
    }
    return CRT(cnt, a, mp);
  }

  math() {
    OLS(), ff[0] = 1;
  }
} st;

int n, mod;

int main() {
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n >> mod;
  st.breakdown(mod);
  cout << ((st.exLucas(n + n, n, mod) - st.exLucas(n + n, n - 1, mod)) % mod + mod) % mod << '\n';
  return 0;
}
posted @ 2024-08-06 21:10  yabnto  阅读(5)  评论(0编辑  收藏  举报