hdu 6053-莫比乌斯反演

题目链接:点击打开链接


题解思路:应用莫比乌斯就是处理容斥,这里用原始的容斥肯定超时。


代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
int n,m;
const int mx = 1e5+10,mod = 1e9+7;
int mu[mx],pri[mx],num[mx],sum[mx<<1];
bool vis[mx];
void init(){
    int top = 0;
    for(int i=2;i<mx;i++){
        if(!vis[i]){
            pri[top++] = i;
            mu[i] = -1;
        }
        for(int j=0;j<top&&pri[j]*i<mx;j++){
            int val = i*pri[j];
            vis[val] = 1;
            if(i%pri[j]==0){
                mu[val] = 0;
                break;
            }
            mu[val] = -mu[i];
        }
    }
}
ll kuaisu(ll x,ll y){
    ll ans = 1;
    while(y){
        if(1&y) ans = ans*x%mod;
        y >>= 1;
        x = x*x%mod;
    }
    return ans;
}
int main(){
    int t,cas = 1;
    scanf("%d",&t);
    init();
    while(t--){
        memset(sum,0,sizeof(sum));
        scanf("%d",&n);
        int minn = 1e9,maxx = 0;
        for(int i=1;i<=n;i++){
            scanf("%d",num+i);
            minn = min(minn,num[i]);
            maxx = max(maxx,num[i]);
            sum[num[i]]++;
        }
        for(int i=mx-2;i>=0;i--) sum[i] += sum[i+1]; //大等i的有几个
        ll ans =0;
        for(int i=2;i<=minn;i++){
            ll cnt = 1,ret = -mu[i];
            int p = i;
            while(p<=maxx)
            ret = (ret*kuaisu(cnt,sum[p]-sum[p+i]))%mod, p += i,cnt++;
            ans = (ans+ret+mod)%mod;
        }
        printf("Case #%d: %I64d\n",cas++,ans);
    }
    return 0;
}

也可以用递归形式的容斥可行


代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
int n,m,minn,maxx;
const int mx = 1e5+10,mod = 1e9+7;
int pri[mx],num[mx],sum[mx<<1];
bool vis[mx];
void init(){
    int top = 0;
    for(int i=2;i<mx;i++){
        if(!vis[i]) pri[top++] = i;
        for(int j=0;j<top&&pri[j]*i<mx;j++){
            vis[i*pri[j]] = 1;
            if(i%pri[j]==0) break;
        }
    }
}
ll kuaisu(ll x,ll y){
    ll ans = 1;
    while(y){
        if(1&y) ans = ans*x%mod;
        y >>= 1;
        x = x*x%mod;
    }
    return ans;
}
ll dfs(int cnt,int pos,ll val){
    ll ans = 0;
    for(int i = pos+1;val*pri[i]<=minn;i++){
        ll s = val*pri[i],ret = 1;
        int p = s,c = 1;
        while(p<=maxx) ret = (ret*kuaisu(c++,sum[p]-sum[p+s]))%mod, p += s;
        if(cnt&1) ans = (ans+ret)%mod;
        else ans = (ans-ret+mod)%mod;
        ans = (ans+dfs(cnt+1,i,s))%mod;
    }
    return ans;
}
int main(){
    int t,cas = 1;
    scanf("%d",&t);
    init();
    while(t--){
        memset(sum,0,sizeof(sum));
        scanf("%d",&n);
        minn = 1e9,maxx = 0;
        for(int i=1;i<=n;i++){
            scanf("%d",num+i);
            minn = min(minn,num[i]);
            maxx = max(maxx,num[i]);
            sum[num[i]]++;
        }
        for(int i=mx-2;i>=0;i--) sum[i] += sum[i+1]; //大等i的有几个
        printf("Case #%d: %lld\n",cas++,dfs(1,-1,1));
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值