码迷,mamicode.com
首页 > 其他好文 > 详细

SCUT - 157 - CC和他的GCD - 容斥原理

时间:2019-06-14 23:54:11      阅读:235      评论:0      收藏:0      [点我收藏+]

标签:return   原理   for   scanf   amp   clu   数字   etc   break   

https://scut.online/p/157

鉴于多年(都没几个月)搞数论的经验,这种时候枚举g肯定是对的。

那么肯定是要莫比乌斯函数作为因子,因为很显然?

但是为什么要搞个负的呢?其实是因为这个题目的g==1的时候并不都是合法的,反而是g==2的时候都是合法的,所以g==6的时候才是重复的。

然后考虑怎么统计他们的倍数。

每次都因数分解,是很慢的。

考虑到这题的特征,数字特别小。用个cnt把每个数字都数一数。

然后从小的数字开始把它所有的倍数都加在它身上。

最后预处理一波组合数就可以了。

整体复杂度每次是\(O(nlogn)\)的,比\(O(n\sqrt{n})\)快了近10倍。

不过最后还是快读最快啊233!

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int MAXN=1e5;

int pri[MAXN+5];
int &pritop=pri[0];
int mu[MAXN+5];
void sieve(int n=MAXN) {
    mu[1]=1;
    for(int i=2; i<=n; i++) {
        if(!pri[i]) {
            pri[++pritop]=i;
            mu[i]=-1;
        }
        for(int j=1; j<=pritop; j++) {
            int &p=pri[j];
            int t=i*p;
            if(t>n)
                break;
            pri[t]=1;
            if(i%p) {
                mu[t]=-mu[i];
            } else {
                mu[t]=0;
                break;
            }
        }
    }
}

const int mod=1e9+7;

int inv[MAXN+5],fac[MAXN+5],invfac[MAXN+5];

void init_fac_invfac(int n=MAXN) {
    inv[1]=1;
    for(int i=2; i<=n; i++)
        inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod;
    fac[0]=1,invfac[0]=1;
    for(int i=1; i<=n; i++) {
        fac[i]=1ll*fac[i-1]*i%mod;
        invfac[i]=1ll*invfac[i-1]*inv[i]%mod;
    }
}

inline ll C(ll n,ll m) {
    if(n<m)
        return 0;
    return 1ll*fac[n]*invfac[n-m]%mod*invfac[m]%mod;
}

inline int read(){
    int x=0;
    char c=getchar();
    while(c<'0'||c>'9')
        c=getchar();
    do{
        x=(x<<3)+(x<<1)+c-'0';
        c=getchar();
    }while(c>='0'&&c<='9');
    return x;
}

inline void write(int x){
    if(x>9){
        write(x/10);
    }
    putchar(x%10+'0');
    return;
}

int cnt[MAXN+5];
int main() {
#ifdef Yinku
    freopen("Yinku.in","r",stdin);
#endif // Yinku
    sieve();
    init_fac_invfac();
    int n,m;
    while(~scanf("%d%d",&n,&m)) {
        memset(cnt,0,sizeof(cnt));
        for(int i=1; i<=n; i++) {
            int tmp=read();
            cnt[tmp]++;
        }
        for(int d=2;d<=100000;d++){
            for(int td=d+d;td<=100000;td+=d)
                cnt[d]+=cnt[td];
        }
        ll sum=0;
        for(int d=2; d<=100000; d++) {
            if(mu[d]==0||cnt[d]<m)
                continue;
            ll tmp=(-mu[d])*C(cnt[d],m);
            if(tmp>=mod||tmp<=-mod) {
                tmp%=mod;
            }
            if(tmp<0)
                tmp+=mod;
            sum+=tmp;
            if(sum>=mod)
                sum-=mod;
        }
        write((int)sum);
        puts("");
    }
}

SCUT - 157 - CC和他的GCD - 容斥原理

标签:return   原理   for   scanf   amp   clu   数字   etc   break   

原文地址:https://www.cnblogs.com/Yinku/p/11025889.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!