21.7.15
tag:组合计数,推柿子,莫队
第一问很简单,等价于合法的操作序列个数。
第二问要推柿子。
问了2个人加上题解一共4个不一样的柿子(虽然都是等价的
sol 1
\(4\) 倍常数,自然就挂成了 \(70\)。
假设 \(n\) 越界(这里就是 \(2\) 倍常数了)
然后设(这里又是 \(2\) 倍常数)
只要我们能 \(O(1)\) 计算出当 \(n/m\) 变化 \(1\) 时新的 \(f,g\),就可以用莫队做了。
\(m\) 变化 \(1\) 时直接加上一项就行了,主要在于 \(n\)。
然后用拆组合数\(\binom nm=\binom {n-1}m+\binom{n-1}{m-1}\)的办法可以推出
然后tle70。
sol 2
上面那 \(2\) 个式子不优美,考虑用一个式子,实际上只用求出到达边界时的期望步数,再用 \(n+m\) 减去。
然后就可以分别莫队了(这里有 \(2\) 倍常数)
所以问题在于 \(\sum_{i=0}^{n+m}\frac{\binom in}{2^{i+1}}i\)。
为了方便,可以令 \(i=(i+1)-1\)。
然后有:
所以就把 \(i\) 给处理掉了。
然后就变成了两个形如 \(\sum_{i=0}^A\frac{\binom iB}{2^{i}}\) 的式子,推法类似。(没错现在又变成了 \(4\) 倍常数)
sol 3
显然 \(4\) 倍常数不太优美
这里有一个性质
根据组合意义,可以看作是一个 \((0,0)\to(n,m)\) 的矩形,然后枚举走的步数 \(i\),\(\binom in\) 就是从 \((n,i-n)\) 走出这个矩形,\(\binom im\) 就是从 \((i-m,m)\) 走出这个矩形,\(\frac1{2^{i+1}}\) 就是对应的概率。
由于上述枚举的情况是不重不漏的,所以这个式子等价于随机游走走出这个矩形的概率,就是 \(1\)。
所以为了推柿子,可以加一个 \(1\) 再减去
还是根据 \(\binom{i}{n}(i+1)=(n+1)\binom{i+1}{n+1}\)
先看左边,根据之前的性质,有:
而 \(i=0\) 那一项一定是 \(0\),所以左边的式子等于:
于是只需要求右边啦!
设 \(f(n,m)=\sum_{i=0}^{n+m+1}\frac{\binom i{m+1}}{2^i}\)(从 \(0\) 开始是因为不影响结果,同时能方便推柿子)
然后这个柿子在莫队时的变化量可以用 sol 1
的方法求出。
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;
}