21.7.15

tag:组合计数,推柿子,莫队


第一问很简单,等价于合法的操作序列个数。

\[ans=\binom{n+m}n\frac1{2^{n+m}} \]

第二问要推柿子。

问了2个人加上题解一共4个不一样的柿子(虽然都是等价的

sol 1

\(4\) 倍常数,自然就挂成了 \(70\)

假设 \(n\) 越界(这里就是 \(2\) 倍常数了)

\[ans=\sum_{i=0}^{m-1}\binom{n+i}i(\frac12)^{n+1+i}(m-i) \]

然后设(这里又是 \(2\) 倍常数)

\[f(n,m)=\sum_{i=0}^{m-1}\binom{n+i}i(\frac12)^i \]

\[g(n,m)=\sum_{i=0}^{m-1}\binom{n+i}i(\frac12)^ii \]

只要我们能 \(O(1)\) 计算出当 \(n/m\) 变化 \(1\) 时新的 \(f,g\),就可以用莫队做了。

\(m\) 变化 \(1\) 时直接加上一项就行了,主要在于 \(n\)

然后用拆组合数\(\binom nm=\binom {n-1}m+\binom{n-1}{m-1}\)的办法可以推出

\[f(n,m)=2f(n-1,m)-\binom{n+m-1}{m-1}(\frac12)^{m-1} \]

\[g(n,m)=2g(n-1,m)+f(n,m)-m\binom{n+m-1}{m-1}(\frac12)^{m-1} \]

然后tle70。

sol 2

上面那 \(2\) 个式子不优美,考虑用一个式子,实际上只用求出到达边界时的期望步数,再用 \(n+m\) 减去。

\[\sum_{i=0}^{n+m}\frac{\binom in+\binom im}{2^{i+1}}i \]

然后就可以分别莫队了(这里有 \(2\) 倍常数)

所以问题在于 \(\sum_{i=0}^{n+m}\frac{\binom in}{2^{i+1}}i\)

为了方便,可以令 \(i=(i+1)-1\)

然后有:

\[\binom in(i+1)=(n+1)\binom{i+1}{n+1} \]

所以就把 \(i\) 给处理掉了。

然后就变成了两个形如 \(\sum_{i=0}^A\frac{\binom iB}{2^{i}}\) 的式子,推法类似。(没错现在又变成了 \(4\) 倍常数)

sol 3

显然 \(4\) 倍常数不太优美

这里有一个性质

\[\sum_{i=0}^{n+m}\frac{\binom in+\binom im}{2^{i+1}}=1 \]

根据组合意义,可以看作是一个 \((0,0)\to(n,m)\) 的矩形,然后枚举走的步数 \(i\)\(\binom in\) 就是从 \((n,i-n)\) 走出这个矩形,\(\binom im\) 就是从 \((i-m,m)\) 走出这个矩形,\(\frac1{2^{i+1}}\) 就是对应的概率。

由于上述枚举的情况是不重不漏的,所以这个式子等价于随机游走走出这个矩形的概率,就是 \(1\)

所以为了推柿子,可以加一个 \(1\) 再减去

\[-1+\sum_{i=0}^{n+m}\frac{\binom in+\binom im}{2^{i+1}}(i+1) \]

还是根据 \(\binom{i}{n}(i+1)=(n+1)\binom{i+1}{n+1}\)

\[\sum_{i=0}^{n+m}\frac{(n+1)\binom{i+1}{n+1}+(m+1)\binom{i+1}{m+1}}{2^{i+1}} \]

\[\sum_{i=1}^{n+m+1}\frac{(n+1)\binom{i}{n+1}+(m+1)\binom{i}{m+1}}{2^{i}} \]

\[\sum_{i=1}^{n+m+1}\frac{(n+1)\binom{i}{n+1}+(n+1+m-n)\binom{i}{m+1}}{2^{i}} \]

\[(n+1)\sum_{i=1}^{n+m+1}\frac{\binom i{n+1}+\binom i{m+1}}{2^{i}}+\sum_{i=1}^{n+m+1}\frac{(m-n)\binom i{m+1}}{2^i} \]

先看左边,根据之前的性质,有:

\[\sum_{i=0}^{n+m+2}\frac{\binom i{n+1}+\binom i{m+1}}{2^{i+1}}=1 \]

\(i=0\) 那一项一定是 \(0\),所以左边的式子等于:

\[(n+1)\cdot 2(1-\frac{\binom{n+m+2}{n+1}+\binom{n+m+2}{m+1}}{2^{n+m+3}}) \]

于是只需要求右边啦!

\(f(n,m)=\sum_{i=0}^{n+m+1}\frac{\binom i{m+1}}{2^i}\)(从 \(0\) 开始是因为不影响结果,同时能方便推柿子)

然后这个柿子在莫队时的变化量可以用 sol 1 的方法求出。

\[f(n,m+1)=f(n,m)-\frac{\binom{n+m+2}{m+2}}{2^{n+m+2}} \]


sol 3 代码,本地样例 \(2\) 用了 \(3.1s\),样例 \(3\) 用了 \(0.6s\)

#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;

template<typename T>
inline void Read(T &n){
	char ch; bool flag=false;
	while(!isdigit(ch=getchar()))if(ch=='-')flag=true;
	for(n=ch^48;isdigit(ch=getchar());n=(n<<1)+(n<<3)+(ch^48));
	if(flag)n=-n;
}

enum{
    MAXN = 400005
};

int n, MOD;

inline int ksm(int base, int k=MOD-2){
    int res=1;
    while(k){
        if(k&1)
            res = 1ll*res*base%MOD;
        base = 1ll*base*base%MOD;
        k >>= 1;
    }
    return res;
}

inline int inc(int a, int b){
    a += b;
    if(a>=MOD) a -= MOD;
    return a;
}

inline int dec(int a, int b){
    a -= b;
    if(a<0) a += MOD;
    return a;
}

inline void iinc(int &a, int b){a = inc(a,b);}
inline void ddec(int &a, int b){a = dec(a,b);}
inline void upd(int &a, long long b){a = (a+b)%MOD;}

int jc[MAXN], invjc[MAXN], pw[MAXN];
inline int C(int n, int m){return n<m?0:1ll*jc[n]*invjc[m]%MOD*invjc[n-m]%MOD;}

inline void prework(int n){
    jc[0] = 1; for(int i=1; i<=n; i++) jc[i] = 1ll*jc[i-1]*i%MOD;
    invjc[n] = ksm(jc[n]); for(int i=n; i; i--) invjc[i-1] = 1ll*invjc[i]*i%MOD;
    pw[0] = 1; pw[1] = (MOD+1)/2; for(int i=2; i<=n; i++) pw[i] = 1ll*pw[i-1]*pw[1]%MOD;
}

int l, r, f, g;

int bel[MAXN], B;

struct quest{
    int l, r, id;
    quest(int l=0, int r=0, int id=0):l(l),r(r),id(id){}
    inline bool operator <(const quest &k)const{
        if(bel[l]!=bel[k.l]) return bel[l]<bel[k.l];
        if(bel[l]&1) return r<k.r;
        return r>k.r;
    }
}q[300005];
int qcnt, ans[500005];

inline int calc(int n, int m){
    int res=0;
    for(int i=0; i<=n+m+1; i++) upd(res,1ll*pw[i]*C(i,m+1));
    return res;
}

int main(){
    double tt=clock();
    // freopen("ex_tennis3.in","r",stdin);
    // freopen("111.out","w",stdout);
    int Case; Read(Case);
    Read(n); Read(MOD);
    prework(MAXN-1);
    int mx=0;
    for(int i=1; i<=n; i++){
        int opt, N, M;
        Read(opt); Read(N); Read(M);
        mx = max(mx,max(N,M));
        if(opt==1) ans[i] = 1ll*C(N+M,N)*pw[N+M]%MOD;
        if(opt==2) q[++qcnt] = quest(N,M,i);
    }

    B=sqrt(mx)*1.5;
    for(int i=1; i<=mx; i++) bel[i] = (i+B-1)/B;
    sort(q+1,q+qcnt+1);
    l = 0, r = 1, f = pw[2];
    int cnt=0;
    for(int i=1; i<=qcnt; i++){
        int tmp;
        while(l<q[i].l){
            upd(f,1ll*C(l+r+2,r+1)*pw[l+r+2]);
            l++;
        }
        while(l>q[i].l){
            l--;
            ddec(f,1ll*C(l+r+2,r+1)*pw[l+r+2]%MOD);
        }
        while(r<q[i].r){
            ddec(f,1ll*C(l+r+2,r+2)*pw[l+r+2]%MOD);
            r++;
        }
        while(r>q[i].r){
            r--;
            upd(f,1ll*C(l+r+2,r+2)*pw[l+r+2]%MOD);
        }
        int N = q[i].l, M = q[i].r;
        ans[q[i].id] = N+M+1;
        ddec(ans[q[i].id],2ll*(N+1)*dec(1,1ll*inc(C(N+M+2,N+1),C(N+M+2,M+1))*pw[N+M+3]%MOD)%MOD);
        ddec(ans[q[i].id],1ll*dec(M,N)*f%MOD);
    }
    // cerr<<cnt<<'\n';
    for(int i=1; i<=n; i++) printf("%d\n",ans[i]);
    cerr<<(clock()-tt)/CLOCKS_PER_SEC<<'\n';
    return 0;
}
posted @ 2021-07-15 18:57  oisdoaiu  阅读(27)  评论(0编辑  收藏  举报