有趣的数列

// 有趣的数列.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
/*
http://ybt.ssoier.cn:8088/problem_show.php?pid=1661
https://loj.ac/p/10239

我们称一个长度为 2n 的数列是有趣的,当且仅当该数列满足以下三个条件:

1、它是从 1 到 2n 共 2n 个整数的一个排列 {ai};

2、所有的奇数项满足 a1<a3<⋯<a2n−1 ,所有的偶数项满足 a2<a4<⋯<a2n;

3、任意相邻的两项 a2i−1与 a2i(1≤i≤n) 满足奇数项小于偶数项,即:a2i−1<a2i 。

任务是:对于给定的 n,请求出有多少个不同的长度为 2n 的有趣的数列。因为最后的答案可能很大,所以只要求输出答案 modP 的值。

【输入】
只包含用空格隔开的两个整数 n 和 P。

【输出】
仅含一个整数,表示不同的长度为 2n 的有趣的数列个数 modP 的值。

【输入样例】
3 10
【输出样例】
5
【提示】
样例说明

对应的 5 个有趣的数列分别为 {1,2,3,4,5,6},{1,2,3,5,4,6},{1,3,2,4,5,6},{1,3,2,5,4,6},{1,4,2,5,3,6}。

数据范围与提示:

对于 50% 的数据,n≤1000,P≤106 ;
    
对于全部数据,1≤n≤106,2≤P≤109 。
*/

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>



using namespace std;

typedef long long LL;
const int N = 2000010;
int n, p;
int primes[N], cnt;
bool st[N];


void init(int n) {
	for (int i = 2; i <= n; i++) {
		if (!st[i]) primes[cnt++] = i;
		for (int j = 0; primes[j]*i <= n; j++) {
			st[i * primes[j]] = true;
			if (i % primes[j] == 0) break;
		}
	}
}

int qmi(int a, int k) {
	int res = 1;
	while (k) {
		if (k & 1) res = (LL)res * a % p;
		a = (LL)a * a % p;
		k >>= 1;
	}
	return res;
}

int get(int n, int p) {
	int s = 0;
	while (n) {
		s += n / p;
		n /= p;
	}
	return s;
}

int C(int a, int b) {
	int res = 1;
	for (int i = 0; i < cnt; i++) {
		int prime = primes[i];
		int s = get(a, prime) - get(b, prime) - get(a - b, prime);

		res = (LL)res * qmi(prime, s) % p;
	}

	return res;
}


int main()
{
	scanf("%d%d",&n,&p);
	init(n * 2);

	cout << (C(n * 2, n) - C(n * 2, n - 1) + p) % p << endl;

	return 0;
}

posted on 2025-04-07 11:48  itdef  阅读(13)  评论(0)    收藏  举报

导航