链接:戳这里
1485: [HNOI2009]有趣的数列
对应的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)。
题意:额 中文题的话就不需要多说了吧
思路:一开始肯定是懵逼了,毕竟我连基本的dp状态都推不出来,然后因为是已经知道了是卡特兰数了,所以在证明为什么是。首先要求奇数位置升序,偶数位置升序,相邻的奇数偶数项满足a[i*2-1]<a[i*2]。然后我们抽象出一个模型:有2n个人排成一行进入剧场。入场费5元。其中只有n个人有一张5元钞票,另外n人只有10元钞票,剧院无其它钞票,问有多少中方法使得只要有10元的人买票,售票处就有5元的钞票找零?(将持5元者到达视作将5元入栈,持10元者到达视作使栈中某5元出栈) 这个是卡特兰数的经典例子
我们也这样设想一下,1~2*n顺序去填序列,因为放的奇偶位置不一定要相匹配,假设当前奇数位置上填了p个,偶数位置上已经填了q个,我们是不是只要满足p<=q就可以了呢?想一下我们是按顺序去填的序列,但是因为不一定要你一个我一个的去填,而是满足p<=q就可以了,类似于了有一个偶数位置进栈,就有一个奇数位置出栈,类似于那种n*n推导的dp方程式,f(n)=f(0)f(n-1)+f(1)f(n-2)+……+f(n-1)f(0) 可以求出答案,但是这道题n=1e6,所以还是回到C(2*n,n)/(n+1)吧
好了我们知道了答案之后还是不能求出正确的答案,因为要取模,而mod是给定的,不一定是质数,排列组合中有除法,总所周知除法取模会出错。那么我们可以简单化简一下C(2*n,n)/(n+1)==(2*n)*(2*n-1)*...*(n+2)*(n^-1)*((n-1)^-1)*...*(1^-1) 嗯这样是不是好看了一点,可是并没有什么卵用
接下来我们分析一个地方,就是怎么简化这个式子,因为每个数都可以变成一些质数的乘积,我们把(2*n)*(2*n-1)*...*(n+2)*(n^-1)*((n-1)^-1)*...*(1^-1)这些数全部都换成质数相乘,也就是一些质数的num次方的总乘积,质数取模的话应该是ok的,所以要模拟O(n*2)的筛选过程了,具体看代码吧
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<string> #include<vector> #include <ctime> #include<queue> #include<set> #include<map> #include<stack> #include<iomanip> #include<cmath> #define mst(ss,b) memset((ss),(b),sizeof(ss)) #define maxn 0x3f3f3f3f #define MAX 1000100 ///#pragma comment(linker, "/STACK:102400000,102400000") typedef long long ll; typedef unsigned long long ull; #define INF (1ll<<60)-1 using namespace std; int P,n; int vis[2000100],prime[2000100],m,a[2000100],num[2000100]; void init(){ m=0; for(int i=2;i<=n*2;i++){ if(!vis[i]) { prime[++m]=i; a[i]=i; } for(int j=1;j<=m;j++){ if(i*prime[j]>2*n) break; vis[i*prime[j]]=1; a[i*prime[j]]=prime[j]; if(i%prime[j]==0) break; } } } ll qpow(int a,int b,int mod){ ll ans=1; while(b){ if(b%2==1) ans=ans*a%mod; b/=2; a=a*a%mod; } return ans; } int main(){ scanf("%d%d",&n,&P); init(); for(int i=1;i<=n;i++) num[i]--; for(int i=n+2;i<=n*2;i++) num[i]++; for(int i=n*2;i>=2;i--){ if(a[i]!=i){ num[i/a[i]]+=num[i]; num[a[i]]+=num[i]; num[i]=0; } } ll ans=1; for(int i=n*2;i>=2;i--){ ans=ans*qpow(i,num[i],P)%P; } printf("%lld\n",ans); return 0; }