Description
我们称一个长度为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的值。
Input
输入文件只包含用空格隔开的两个整数n和P。输入数据保证,50%的数据满足n≤1000,100%的数据满足n≤1000000且P≤1000000000。
Output
仅含一个整数,表示不同的长度为2n的有趣的数列个数mod P的值。
Sample Input
3 10
Sample Output
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)。
对应的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)。
这题非常蛋疼啊……
如果固定偶数位填入的数,那么各个奇数位也是唯一确定的。因为奇数位也要递增的。
以样例为例,偶数位有{2,4,6},{2,5,6},{3,4,6},{3,5,6},{4,5,6}这五种唯一的方案。
除了要满足严格递增以外,还有一个条件,就是a[i]>=2i。
这个很好yy,因为我们考虑的是偶数位的排列,前面肯定还有奇数位的数,而且大小比它小。
前面的偶数位的数都比它小,前面奇数位的数也比它小,所以前面的数都比它小。那么它至少是2i
问题转变为:有一个长度为n的序列,要往里头填1~2n的数,使得序列严格递增,且对于任意i,有a[i]>=2i
这我只能想到dp:f[i][j]表示前i个位置填了,第i个位置的数大小是j的方案数。转移随便yy,最后f[n][2n]即是所求。这样n^2只有50分。代码见下
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> #include<queue> #include<deque> #include<set> #include<map> #include<ctime> #define LL long long #define inf 0x7ffffff #define pa pair<int,int> #define pi 3.1415926535897932384626433832795028841971 using namespace std; inline LL read() { LL 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; } inline void write(LL a) { if (a<0){printf("-");a=-a;} if (a>=10)write(a/10); putchar(a%10+'0'); } int n,mod; int f[4010][4010]; int s[4010][4010]; inline void writeln(LL a){write(a);printf("\n");} int main() { n=read();mod=read(); for (int i=1;i<=n;i++) for (int j=2*i;j<=2*n;j++) { if (i!=1)f[i][j]=s[i-1][j-1]-s[i-1][2*i-3]; else f[i][j]=1; f[i][j]%=mod;if (f[i][j]<0)f[i][j]+=mod; s[i][j]=s[i][j-1]+f[i][j];s[i][j]%=mod;if (s[i][j]<0)s[i][j]+=mod; } printf("%d\n",f[n][2*n]); }
正解竟然是……
先把前几个打表出来,发现1,2,5,14,42……没错就是卡特兰数
然后用公式算:h[n]=C(2n,n)/(n+1)=(2n)!/(n!(n+1)!)
取模什么的,分解质因数搞搞
为什么是卡特兰数?hzwer:其实就是从左往右扫每个数,把放在奇数项看作入栈,偶数看作出栈
这TM真的是省选题!?
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> #include<queue> #include<deque> #include<set> #include<map> #include<ctime> #define LL long long #define inf 0x7ffffff #define pa pair<int,int> #define pi 3.1415926535897932384626433832795028841971 # using namespace std; inline LL read() { LL 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; } inline void write(LL a) { if (a<0){printf("-");a=-a;} if (a>=10)write(a/10); putchar(a%10+'0'); } inline void writeln(LL a){write(a);printf("\n");} int n,mod,len; LL ans=1; int prime[2000010],rep[2000010],mn[2000010]; bool mrk[2000010]; inline void getprime() { for(int i=2;i<=2*n;i++) { if (!mrk[i])prime[++len]=i,mn[i]=len; for (int j=1;prime[j]*i<=2*n&&j<=len;j++) { mrk[prime[j]*i]=1;mn[prime[j]*i]=j; if (i%prime[j]==0)break; } } } inline void inc(int x,int f) { while (x!=1) { rep[mn[x]]+=f; x/=prime[mn[x]]; } } int main() { n=read();mod=read(); getprime(); for (int i=n+1;i<=2*n;i++)inc(i,1); for (int i=1;i<=n+1;i++)inc(i,-1); for (int i=1;i<=len;i++) while (rep[i]--)ans=(ans*prime[i])%mod; printf("%lld\n",ans); }
——by zhber,转载请注明来源