【2019.8.15 慈溪模拟赛 T2】组合数(binom)(卢卡斯定理+高维前缀和)

卢卡斯定理

题目中说到\(p\)是质数。

而此时要求组合数向质数取模的结果,就可以用卢卡斯定理:

\[C_x^y=C_{x\ div\ p}^{y\ div\ p}\cdot C_{x\ mod\ p}^{y\ mod\ p}\]

也就是说,我们可以把\(x\)\(y\)转化成两个\(p\)进制数,然后每一位分别求组合数后再乘起来。

所以问题来了,什么时候一个组合数的值模\(p\)\(0\)

由于它是质数,所以对于一个组合数\(C_a^b\),当且仅当\(a<b\)时它的值才会为\(0\)

也就是说,对于两个数\(x,y\),只要\(y\)\(p\)进制下有一位的值比\(x\)这一位大,\(C_x^y=0\)

高维前缀和

考虑先容斥。

我们用总方案数(\(n^2\))减去组合数不为\(0\)的组数,就是答案。

\(C_x^y\)不为\(0\),当且仅当\(y\)\(p\)进制下每一位的值都小于等于\(x\)这一位。

是不是想到了高维偏序。。。

然后,这道题就变成裸的高维前缀和了。

如果你不知道高维前缀和,可以去看看我的这篇博客:浅谈高维前缀和

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 1000000
#define LL long long
#define Qinv(x) Qpow(x,X-2)
using namespace std;
int n,X,a[N+5],s[N+5];
class FastIO
{
    private:
        #define FS 100000
        #define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
        #define tn (x<<3)+(x<<1)
        #define D isdigit(c=tc())
        char c,*A,*B,FI[FS];
    public:
        I FastIO() {A=B=FI;}
        Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
}F;
int main()
{
    freopen("binom.in","r",stdin),freopen("binom.out","w",stdout);
    RI i;LL p,ans=0;for(F.read(n),F.read(X),i=1;i<=n;++i) F.read(a[i]),++s[a[i]];//读入
    for(p=1;p<=N;p*=X) for(i=0;i<=N;++i) (i/p)%X&&(s[i]+=s[i-p]);//高维前缀和
    for(i=1;i<=n;++i) ans+=n-s[a[i]];return printf("%lld",ans),0;//容斥求答案
}

猜你喜欢

转载自www.cnblogs.com/chenxiaoran666/p/Contest20190815T2.html