Skip to content

DTOJ 3987 数学课 - Roor - 博客园

约 819 字大约 3 分钟

卢卡斯定理

2018-02-14

题面

题目描述

wzy又来上数学课了…… 虽然他很菜,但是数学还是懂一丢丢的。老师出了一道题,给定一个包含nn个元素的集合P=1,2,3nP=1,2,3……n求有多少集合APA \subseteq P,满足xAx \in A2xA2x \notin A且对于AAPP中的补集也要满足相同条件。给定mm求大小为mmAA有多少个,输出答案mod 10000019mod~10000019

输入

第一行n,qn,q,接下来qq行,每行一个mm

输出

对于每个mm输出答案mod 10000019mod~10000019

【样例输入】

3 3

0

1

2

【样例输出】

0

2

2

【数据范围】

n,m<=1018 q<=100000n,m<=10^{18}~q<=100000

题解

又是计数类问题,应该有DP写法。 n,mn,m很大,但模数比较小,又可以考虑卢卡斯。

恩,思路比较多。可以先考虑观察找找性质。

xx2x2x不能再同一个集合,我们将将xx2x2x连一条边,那么nn个数,就被划分成了若干条链,然后对于每一条链,发现只有两种选法(间隔一个选一个),而且每条链都必须选其中一种。那么总共有多少种选法就很显然了。

考虑题目中要求的集合大小为mm。不难发现存在无解(即答案为0)。我们假设一条链的链长为2p2p2p+12p+1,那么由于每条链都必须选其中的一半,偶数链选出的个数一定为pp,但奇数链可以选出ppp+1p+1。所以可以计算出 最少要取几个(下界)最多可以取几个(上界) ,显然,不在这范围内的mm就无解了。那么考虑有解情况,偶数链都选pp不会改变选出数的个数,只有奇数链可以取ppp+1p+1可以影响选出数的个数。我们用mm减去下界,然后剩下的数就需要用奇数链中p+1p+1多出来的11补齐,那么就考虑是那几条奇数链补上的一即可。

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #define ll long long
    #define p 10000019
    using namespace std;
    int q,logn;
    ll n,m,f[p+5],inv[p+5],l,r,las,mi[70],odd,eve;
    inline ll power(ll a,ll x){
        ll res=1;
        for(;x;a=a*a%p,x>>=1) if(x&1) res=res*a%p;
        return res;
    }
    inline ll C(ll a,ll b){
        return a<b?0:f[a]*inv[f[b]]%p*inv[f[a-b]]%p;
    }
    inline ll lucas(ll a,ll b){
        if(a<p&&b<p) return C(a,b);
        return C(a%p,b%p)*lucas(a/p,b/p)%p;
    }
    int main(){
        scanf("%lld%d",&n,&q);
        inv[1]=f[0]=1;
        for(int i=1;i<p;i++) f[i]=f[i-1]*i%p;
        for(int i=2;i<p;i++) inv[i]=inv[p%i]*(p-p/i)%p;
        for(ll i=n;i;i>>=1) logn++;
        mi[0]=1;for(int i=1;i<=logn;i++) mi[i]=mi[i-1]*2;
        for(int j=logn;j;j--){
            ll now=n/mi[j-1];
            ll num=(now+1)/2-las;
            las=(now+1)/2;
            l+=j/2*num;r+=(j+1)/2*num;
            j&1?odd+=num:eve+=num;
        }
        for(int i=1;i<=q;i++){
            scanf("%lld",&m);
            if(m<l||m>r){puts("0");continue;}
            else printf("%lld\n",lucas(odd,m-l)*power(2,eve)%p);
        }
        return 0;
    }

总结:

计数类问题真的很多都是 排列组合DP。 这道题也有DP推导的方法。