容斥原理!!!
题目描述
我们称一个长度为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的有趣的数列。因为最后的答案可能很大,所以只要求输出答案 mod P的值。
此题还是很简单,我们首先需要知道为什么是catalan数列。
* A1<A3<A5<A7
* A2<A4<A6<A8
* 对于A4来说它一定大于前三个,对于A3来说它一定小于后五个
* 所以可以推断奇数项 Ai<i.
* 至于为什么是Catalan?
* 其实就是从左往右扫每个数,把放在奇数看作入栈,偶数看作出栈;也可以用左(奇数)右(偶数)括号匹配来理解
* {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}的括号匹配如下:
* (=135,()()();(=134,()(());(=125,(())();(=124,(()());(=123,((()))
但是我们发现,这个p并不是一个素数,所以我们没法求逆元
但我们知道catalan公式:F(n)=C(2*n,n)/(n+1)=(2n!)/(n!(n+1)!)。
因为取模很麻烦,所以我们可以想到将每一个非素数的数,拆成几个素数之积,然后就可以计算出每一个素数被使用了多少次。进而进行快速幂计算答案.
#pragma GCC target("f16c") #pragma GCC optimize("unroll-loops") #pragma GCC target("sse3","sse2","sse") #pragma GCC optimize(3,"Ofast","inline") #pragma GCC optimize("no-stack-protector") #pragma GCC target("avx","sse4","sse4.1","sse4.2","ssse3") #include<bits/stdc++.h> using namespace std; #define INF 0x3f #define ll long long #define eps 1e-5 const int N=2e6+8; int n,p,m,ans; int tot,prime[N]; bool visit[N]; inline void pri(int n) { for(int i=2;i<=n;i++) { if(!visit[i]) prime[++tot]=i; for (int j=1,k;j<=tot&&(k=prime[j]*i)<=n;j++) { visit[k]=1; if (i%prime[j]==0) break; } } } inline int ksm(int a,int b) { int sum=1; while(b){ if (b&1) sum=1LL*sum*a%p; a=1LL*a*a%p; b>>=1; } return sum; } int main(){ scanf("%d%d",&n,&p); pri(m=n<<1); ans=1; for(int i=1;i<=tot;i++) {//计算出每一个素数被使用了多少次,后直接计算其快速幂. int s=0; for(int j=m;j/=prime[i];s+=j) ; for(int j=n;j/=prime[i];s-=j) ; for(int j=n+1;j/=prime[i];s-=j) ;//容斥原理.求出每一个素数被计算了多少次,把不是素数的数拆成素数,后计算素数的快速幂. //此处后两个循环是减,因为公式是(2n!)/(n!(n+1)!),对于一个质数来说,他就需要出去他在n!与(n+1)!中被计算的次数. if(s) ans=1LL*ans*ksm(prime[i],s)%p; } printf("%d",ans); return 0; } /* Catalan,因P不是质数,不能用逆元 * A1<A3<A5<A7 * A2<A4<A6<A8 * 对于A4来说它一定大于前三个,对于A3来说它一定小于后五个 * 所以可以推断奇数项 Ai<i. * F(n)=C(2*n,n)/(n+1)=(2n!)/(n!(n+1)!) * 分解质因数求即可,用动规解只能得50分 * 至于为什么是Catalan? * 其实就是从左往右扫每个数,把放在奇数看作入栈,偶数看作出栈;也可以用左(奇数)右(偶数)括号匹配来理解 * {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}的括号匹配如下: * (=135,()()();(=134,()(());(=125,(())();(=124,(()());(=123,((())) */