The 2017 China Collegiate Programming Contest, Qinhuangdao Site B

题目链接:ZOJ3982 Expected Waiting Time

sol:

  1. 对于固定的 A S AS AS序列,无论离开休息室的顺序如何,总的等待时长是相同的。一个合法的 A S AS AS序列满足其所有的前缀中 A A A的个数 ≥ \ge S S S的个数,等价于一个长度为 2 n 2n 2n的括号序列。易知总方案数为第 n n n个卡特兰数,记为 H ( n ) H(n) H(n)

  2. 现在考虑计算每一个人的贡献。把问题转换为括号序列,则总等待时长相当于在左括号的位置标记 − - 号,在右括号的位置标记 + + +号,求 a [ i ] a[i] a[i]乘上 + / − +/- +/号后的前缀和。

  3. 若我们固定一对合法括号 l , r l,r l,r,整个括号序列序列当且仅当 [ l , r ] [l,r] [l,r]区间合法且 [ 1 , l − 1 ] [1,l-1] [1,l1] [ r + 1 , 2 n ] [r+1,2n] [r+1,2n]合法。则总的方案数为 H ( r − l + 1 2 − 1 ) ∗ H ( 2 n − ( r − l + 1 ) 2 ) H(\frac{r-l + 1}{2} - 1) * H(\frac{2n - (r-l+1)}{2}) H(2rl+11)H(22n(rl+1)),而这对括号的贡献为 a [ r ] − a [ l ] a[r] - a[l] a[r]a[l]

  4. 考虑枚举长度,则总等待时间为
    t o t = ∑ L = 2 2 n ∑ i = 1 2 n + 1 − L H ( L 2 − 1 ) H ( 2 n − L 2 ) ( a [ i + L − 1 ] − a [ i ] ) = ∑ L = 2 2 n H ( L 2 − 1 ) H ( 2 n − L 2 ) ∑ i = 1 2 n + 1 − L ( a [ i + L − 1 ] − a [ i ] ) = ∑ L = 2 2 n H ( L 2 − 1 ) H ( 2 n − L 2 ) f ( L ) \begin{aligned} tot &= \sum_{L=2}^{2n} \sum_{i = 1}^{2n+1-L} H(\frac{L}{2} - 1) H(\frac{2n - L}{2}) (a[i+L-1] - a[i]) \\ &= \sum_{L=2}^{2n} H(\frac{L}{2} - 1) H(\frac{2n - L}{2})\sum_{i = 1}^{2n+1-L} (a[i+L-1] - a[i]) \\ &= \sum_{L=2}^{2n} H(\frac{L}{2} - 1) H(\frac{2n - L}{2})f(L) \end{aligned} tot=L=22ni=12n+1LH(2L1)H(22nL)(a[i+L1]a[i])=L=22nH(2L1)H(22nL)i=12n+1L(a[i+L1]a[i])=L=22nH(2L1)H(22nL)f(L)

答案为 a n s = t o t H ( n ) ans = \frac{tot}{H(n)} ans=H(n)tot
看起来复杂度比较感人。
右边式子化简一下
f ( L ) = ∑ i = 1 2 n + 1 − L ( a [ i + L − 1 ] − a [ i ] ) = ∑ i = 1 2 n + 1 − L − ∑ L 2 n \begin{aligned} f(L) &=\sum_{i = 1}^{2n+1-L} (a[i+L-1] - a[i])\\ &= \sum_{i=1}^{2n+1-L} - \sum_{L}^{2n} \end{aligned} f(L)=i=12n+1L(a[i+L1]a[i])=i=12n+1LL2n
发现是个前缀和后缀的形式, O ( n ) O(n) O(n)预处理一下,于是整个式子就可以 O ( n ) O(n) O(n)计算了。

  1. 计算卡特兰数可以用
    H ( n ) = H ( n − 1 ) 4 n − 2 n + 1 H(n) = H(n-1)\frac{4n-2}{n+1} H(n)=H(n1)n+14n2递推计算。

code:

#include
using namespace std;

typedef long long ll;
const int maxn = 2e6+10;

ll a[maxn],b[maxn];
ll h[maxn],inv[maxn];
ll sum[maxn];
int n,mod;

void Add(ll &x,ll y){
    x += y;
    if(x>=mod) x-=mod;
}

void Mul(ll& x,ll y){
    x *= y;
    if(x>=mod) x %= mod;
}

void init(){
    inv[0] = inv[1] = 1;
    for(int i = 2;i<= n *2 ;i++) inv[i] = inv[mod % i] * (mod - mod/i) % mod;
    h[0] = h[1] = 1;
    for(int i = 2;i<= n * 2;i++) h[i] = h[i-1] * (i * 4 - 2) % mod * inv[i+1] % mod;
}

ll qpow(ll a,int b){
    ll ret = 1;
    while(b){
        if(b&1) Mul(ret,a);
        Mul(a,a);
        b>>=1;
    }
    return ret;
}

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        int A,B;
        scanf("%d%d%lld%d%d",&n,&mod,&b[0],&A,&B); 
        init();
        for(int i = 1;i<=n*2;i++) b[i] = (b[i-1] * A + B) % mod;
        for(int i = 1;i<=n*2;i++){
            a[i] = (a[i-1] + b[i] + 1) % mod;
            // cout<<"a[i] = "<
        }
        for(int i = 1;i<=n*2;i++){
            sum[i] = sum[i-1];
            Add(sum[i],a[i]);
        }
        ll ans = 0;
        for(int i = 1;i<=n;i++){
            ll ret = sum[n*2];
            Add(ret,mod - sum[i*2 - 1]);
            Add(ret,mod - sum[n*2 + 1 - i*2]);
            // cout<<"ret1 = "<
            Mul(ret,h[i-1]);
            Mul(ret,h[n-i]);
            Add(ans,ret);
            // cout<<"ret2 = "<
        }
        Mul(ans,qpow(h[n],mod-2));
        printf("%lld\n",ans);
    }
    return 0;
}

你可能感兴趣的:(卡特兰数,组合数学)