AT_tenka1_2014_final_d 高桥君 解题报告

AT_tenka1_2014_final_d 高桥君 解题报告

简要题意

\(m\) 次询问,每次给出 \(n,k\),求

\[\sum_{i=0}^k \binom{n}{i} \pmod {10^9+7} \]

其中,\(n,m,k \le 10^5\)

分析

首先,我们都知道怎么通过 \(O(n)\) 预处理来实现 \(O(1)\) 求组合数。

都是直接统计是 \(O(n^2)\) 的,非常不可做。

考虑到要求的东西是一个类似区间和的东西,考虑分块。

具体的,一个块 \([L,R]\) 维护 \(\sum_{i=L}^R \binom{n}{i}\)\(O(n)\) 维护,\(O(\sqrt n)\) 询问是简单的。

都是对每一个 \(n\) 都来一遍不现实,考虑怎么由一个状态转到另一个状态。

其次,我们需要用到这个式子:

\[\binom{n}{m}+\binom{n}{m+1}=\binom{n+1}{m+1} \]

我们考虑当我们由 \(n\) 转移到 \(n-1\) 时,存在:

\[\begin{aligned} \sum_{i=L}^R \binom{n}{i} & = \sum_{i=L}^R \binom{n-1}{i} + \sum_{i=L}^R\binom{n-1}{i-1} \\ & = \sum_{i=L}^R \binom{n-1}{i} + \sum_{i=L}^R\binom{n-1}{i} - \binom{n-1}{R} +\binom{n-1}{L-1} \\ & = 2 \times \sum_{i=L}^R \binom{n-1}{i} - \binom{n-1}{R} +\binom{n-1}{L-1} \end{aligned} \]

也就是:

\[\sum_{i=L}^R \binom{n-1}{i}=\frac{\sum_{i=L}^R \binom{n}{i} + \binom{n-1}{R} - \binom{n-1}{L-1}}{2} \]

利用这个式子维护块的和就可以了

代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define For(i,s,t) for(int i=s;i<=t;++i)
#define Down(i,s,t) for(int i=s;i>=t;--i)
#define ls (i<<1)
#define rs (i<<1|1)
#define lowbit(x) ((x)&(-(x)))
#define add(x,y) ((1ll*x+y)%p)
using namespace std;
typedef pair<int,pair<int,int> > piii;
typedef long long ll;
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}
inline ll min(ll x,ll y){return x<y?x:y;}
inline ll max(ll x,ll y){return x>y?x:y;}
inline int read(){
    register int x=0;
    char c=getchar();
    while(c<'0' || '9'<c) c=getchar();
    while('0'<=c && c<='9') x=(x<<1)+(x<<3)+c-'0',c=getchar();
    return x;
}
void write(int x){
    if(x<0){putchar('-');x=-x;}
    if(x>=10) write(x/10);
    putchar(x%10+'0');
}
const int N=2e5+1000,M=1e5,SZ=500,p=1e9+7;
int T,n,k,fac[N],inv_fac[N],inv[N],ans[N];
inline int C(int n,int m){
    return (m && m<=n) ? 1ll*fac[n]*inv_fac[m]%p*inv_fac[n-m]%p : 0;
}
void init(){
    fac[0]=inv_fac[0]=inv[1]=fac[1]=inv_fac[1]=1;
    For(i,2,M){
        fac[i]=1ll*fac[i-1]*i%p;
        inv[i]=(p-(ll)(p/i)*inv[p%i]%p)%p;
        inv_fac[i]=1ll*inv[i]*inv_fac[i-1]%p;
    }
}
int L[SZ],R[SZ],sz=317,val[N],cnt,nw=M;
void divide(){
    while(R[cnt]!=nw){
        ++cnt;
        L[cnt]=R[cnt-1]+1;
        R[cnt]=min(L[cnt]+sz-1,M);
        For(i,L[cnt],R[cnt])
            val[cnt]=add(val[cnt],C(M,i));
    }
}
void go_next(){
    --nw;
    For(i,1,cnt){
        int delta=(1ll*C(nw,R[i]) - C(nw,L[i]-1) +p)%p;
        val[i]=1ll*add( val[i] ,  delta )*inv[2]%p;
    }
    --R[cnt];
    if(R[cnt]<L[cnt]) --cnt;
}
int qpow(int x,int y){
    int ans=1;
    while(y){
        if(y&1)
            ans=1ll*ans*x%p;
        x=1ll*x*x%p;
        y>>=1;
    }
    return ans;
}
struct Node{int x,k,id;}q[N];
bool cmp(Node x,Node y){return x.x>y.x;}
void solve(int pos,int id){
    if(pos>nw) return;
    //printf("%d %d\n",pos,cnt);
    int res=0,ind=cnt;
    while(pos<L[ind]){
        res=add(res,val[ind]);
        ind--;
    }
    For(i,pos,R[ind]) res=add(res,C(nw,i));
    ans[id]=(1ll*ans[id]-res+p)%p;
}
int main()
{
    freopen("star.in","r",stdin);
    freopen("star.out","w",stdout);
    T=read();
    init();
    divide();
    For(i,1,T) q[i].x=read(),q[i].k=read()+1,q[i].id=i,ans[i]=qpow(2,q[i].x);
    sort(q+1,q+T+1,cmp);
    //For(i,1,T)
    //    printf("%d %d %d\n",q[i].x,q[i].k,q[i].id);
    int ind=1;
    Down(i,M,1){
        while(ind<=T && q[ind].x==i) solve(q[ind].k,q[ind].id),ind++;
        if(i!=1) go_next();
    }
    For(i,1,T)
        printf("%d\n",ans[i]);
    return 0;
}
posted @ 2025-07-18 14:45  XiaoZi_qwq  阅读(6)  评论(0)    收藏  举报