Codeforces Round #450 (Div. 2)D. Unusual Sequences【组合数学】【容斥】

题目链接:https://codeforc.es/contest/900/problem/D

题目大意:要你构造一个长度为nn(未知)的数组,使得ai>=1a_i>=1gcd(a1,a2,,an)=xgcd(a_1,a_2,\dots,a_n)=xi=1nai=y\sum_{i=1}^na_i=y,输出种类数。

思路:从gcd(a1,a2,,an)=xgcd(a_1,a_2,\dots,a_n)=xi=1nai=y\sum_{i=1}^na_i=y我们可以推断出对于构造的aa数组,每个数都是xx的倍数,且所有数的和为yy,由此可以联想到类似于隔板法:将yx\frac{y}{x}份至少分成两份,最多分成yx\frac{y}{x}份,不能为空

这样总的分类数即为:令cntcnt为份数,i=2cnt1Ccnt1i=2cnt11\sum_{i=2}^{cnt-1}C_{cnt-1}^{i}=2^{cnt-1}-1

但是我们注意到单纯这样分是存在问题的,例如:x=2,y=8x=2,y=8,我们可能会分出{4,4}\{4,4\}这样错误的情况,所以我们需要去除掉不合法的种类数,我们可以发现:不合法的种类数产生的原因为:份数cntcntyx\frac{y}{x}的因子,所以我们减掉份数cntcntyx\frac{y}{x}的因子的值就okok了。

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
ll qpow(ll x,ll y){
    ll ans=1;
    while(y){
        if(y&1){
            ans=ans*x%mod;
        }
        x=x*x%mod;
        y>>=1;
    }
    return ans;
}
map<ll ,ll >mp;
ll dfs(ll x){
    if(mp[x]){
        return mp[x];//用map映射直接返回,减少多余计算
    }
    if(x==1){
        return 1;
    }
    mp[x]=(qpow(2,x-1)-1+mod)%mod;
    for(ll i=2;i*i<=x;i++){
        if(x%i==0){
            mp[x]=(mp[x]-dfs(x/i)+mod)%mod;
            if(i*i!=x){
                mp[x]=(mp[x]-dfs(i)+mod)%mod;
            }
        }
    }
    return mp[x];
}
int main()
{
    ll x,y;
    cin>>x>>y;
    if(y%x!=0){//序列总和不是gcd的倍数
        cout<<0<<endl;
    }
    else{
        ll cnt=y/x;
        ll ans=dfs(cnt);
        cout<<ans<<endl;
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章