HDU 5514(Frogs-与n互质的数的求和)

给一个数列 ai|m(1m109) ,
<m <script type="math/tex" id="MathJax-Element-5724"> ai 的倍数的数,求和

先把重复的数,倍数关系的数去掉。
显然m的因子不超过200个,令 D m的因子集合
此时 ai 均为 m 的因子,
赛场上可以O(2n) 暴力
但hdu上不行

考虑 O(|D|2) 的算法

我们把 [0,m1] 的整数 x , 按gcd(x,m)=d分类
显然每个集合中的数要么全取,要么不取
对每个d,取的代价是 d *(小于m/d且与它互质的数的和)
d=dphi(m/d)m/d/2=mphi(m/d)/2

如何对 <t <script type="math/tex" id="MathJax-Element-5738"> ai 求和?
注意由于 gcd(a,b)=gcd(ab,b)
答案= tϕ(t)2

#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<functional>
#include<cmath>
#include<vector>
#include<map>
#include<iostream>
#include<queue>
using namespace std;
typedef long long ll;
#define For(i,n) for(int i=1;i<=n;i++)
#define Rep(i,n) for(int i=0;i<n;i++)
#define Fork(i,k,n) for(int i=k;i<=n;i++)
#define pb push_back
#define MAXN (1000000)

int gcd(int a,int b){if (!b) return a; else return gcd(b,a%b);}
int lcm(int a,int b){if (a==0) return b; return a/gcd(a,b)*b;}
int n,m,a[MAXN];
vector<int> v;
vector<int> v2;
bool b[MAXN];
ll ans=0;
int t2;
void dfs(int l,int x,int s) {
    if (l==t2) {
        if (x==0) return;
        ll p2=(ll)x*(1+(ll)(m-1)/x)*(ll)((m-1)/x)/2;
        if (s&1) ans+=p2; else ans-=p2;
        return;
    }
    dfs(l+1,lcm(x,v2[l]) ,s+1);
    dfs(l+1,x,s);
}
ll phi(int m){
    if (m==1) return 1;
    int ans=m;
    for(int d=2;d*d<=m;d++) {
                if (m%d==0) {
                    ans=ans*(ll)(d-1)/(ll)d;
                } 
                while (m%d==0) m/=d;
        }
    if (m>1) ans=ans*(ll)(m-1)/(ll)m;
    return ans;
}
ll sum_phi(int t)
{
}
map<int,int> h;
int main() {
    int T;cin>>T;
    For(kcase,T)
    {
        v.clear();
        v2.clear();
        ans=0;
        cin>>n>>m;
        For(i,n) scanf("%d",&a[i]),a[i]=gcd(a[i],m);
        sort(a+1,a+1+n);
        n=unique(a+1,a+1+n)-(a+1);


        if (a[1]==1) {
            printf("Case #%d: %lld\n",kcase,(ll)(m)*(ll)(m-1)/2);
            continue;
        }

        for(int d=1;d*d<=m;d++) {
                if (m%d==0) {
                    v.pb(d);
                    if (d*d<m) v.pb(m/d);
                } 
        }
        int t=v.size();
        Rep(j,t) b[j]=0;

        h.clear();
        For(i,n) {
            if (h.find(a[i])!=h.end()) continue;
            Rep(j,t) {
                if (b[j]) continue;
                if (a[i]==v[j]) {
                    v2.pb(a[i]);
                }
                if (v[j]%a[i]==0) {
                    b[j]=1;
                    h[v[j]]=1;
                }
            }
        }


        t2=v2.size();
//        Rep(i,t2) cout<<v2[i]<<' ';cout<<endl;
//        Rep(i,t) cout<<v[i]<<' ';cout<<endl;

        // For(i,100) cout<<phi(i)<<' ';cout<<endl;

        Rep(i,t) {
            Rep(j,t2) {
                if (v[i]%v2[j]==0) {
                    ll d=v[i];
                    if (m/d==1) ans+=0;
                    else ans+= (ll)(phi(m/d))*m/2;
                    //cout<<v[i]<<' '<<ans<<endl;
                    break;
                }
            }
        }    


        printf("Case #%d: %lld\n",kcase,ans);
    }

    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值