题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3982
oj自带十倍常数,我也是很服气的
首先我们不难发现,每次选的出去的选手无论是谁,只要AS序列固定,总体的等待时间是不变的,所以,我们可以指定每次选的人是最后进房间的,设第i个卡特兰数为h(i),那么方案总数就是卡特兰数h(n)
然后理性分析一下,肯定是要按照位置算贡献的,对于每一个位置有两种情况,出栈和入栈,假设位置i是出栈的情况,那么我们枚举所有可能的入栈位置j(j<i),很容易发现,假设j与i之间有k个位置,那么如果某个元素从j入栈,从i出栈,那么j和i之间的k的个位置必须满足中间的所有元素入栈一遍,出栈一遍,很显然,k一定是个偶数,而且方案数为h(k/2),然后对于剩下的2*n-k-2个元素,他们的入栈出栈的方案数为h((2*n-k-2)/2),则对于位置i出栈,他的贡献为所有满足条件的k,sigma(h(k/2)*h((2*n-k-2)/2)),同理我们可以得到i为入栈时候的贡献,只不过这时候的贡献是负的。然后我们发现,当我们计算出对于i出栈的所有情况之后,i+2出栈的所有情况只比i出栈的所有情况多了一项,那么我们就可以dp方案数,然后每个位置算贡献就好了。
代码:
#include<bits/stdc++.h> using namespace std; const int MAXN=1e6+5; typedef long long ll; ll a[MAXN*2],b[MAXN*2],f[MAXN],h[MAXN],dp[MAXN*2]; inline ll qpow(int a,int b,int p) { ll ret=1; for(a%=p;b;b>>=1) { if(b&1) ret=1LL*ret*a%p; a=1LL*a*a%p; } return ret; } void init(int n,int MOD) { f[0]=0;f[1]=1; for(int i=2;i<=n+1;i++) f[i]=(1LL*((MOD-MOD/i)%MOD)*(f[MOD%i]%MOD))%MOD; h[0]=1;h[1]=1; for(int i=2;i<=n;i++) h[i]=1LL*h[i-1]*(4*i-2)%MOD*f[i+1]%MOD; } void solve() { int n,p,A,B; scanf("%d%d%d%d%d",&n,&p,&b[0],&A,&B); init(n,p); a[0]=0; for(int i=1;i<=2*n;i++) { b[i]=(1LL*A*b[i-1]+B)%p; a[i]=a[i-1]+b[i]+1; } ll ans=0; dp[0]=dp[1]=0; for(int i=2;i<=2*n;i++) { int j=(i>>1)-1; dp[i]=(dp[i-2]+1LL*h[j]*h[n-1-j]%p)%p; ans=(ans+1LL*a[i]%p*dp[i]%p)%p; } for(int i=2*n-1;i>=1;i--) { ans=(ans-1LL*a[i]%p*dp[2*n+1-i]%p+p)%p; } ans=1LL*ans*qpow(h[n],p-2,p)%p; printf("%lld\n",(ans+p)%p); } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int T; scanf("%d",&T); while(T--) { solve(); } return 0; }