HDU 5780 gcd

11 篇文章 0 订阅
7 篇文章 0 订阅

Description

Little White learned the greatest common divisor, so she plan to solve a problem: given x,n,query ni=1nj=1gcd(xi1,xj1)

Solution

你妹这场BC跪成狗。。
T3没判1挂了。。
这道T5也不难,怎么会有点恐惧??

首先的,可以 xi1 可以因式分解为 (x1)i1j=0xj
f(x)=ni=01xi
看后面那个式子,显然对于某 (i,j) , gcd(xi1,xj1)=(x1)gcd(i,j)k=0xk
再把外面的 (x1) 乘进来,得到 gcd(xi1,xj1)=xgcd(i,j)1
化简:
原式

=i=1nj=1nxgcd(i,j)1

=di=1ndj=1nd(xd1)[gcd(i,j)=1]

然后我就不知道自己在干啥了,抛了个莫比乌斯反演进去,得到了一个 O(nlnn) 的做法,被卡掉了。。
事实上因为两个 上限相同,可以直接用欧拉函数(当然莫比乌斯函数抛进去也可以通过卷积化掉变成欧拉函数)。。
继续:
=d(xd1)(i=1nd2φ[i]1)

然后前缀和处理后面的 ,分段处理即可
当然一段 xd1 可以用等比数列公式求和,所以还要快速幂和逆元

果然还是没见过世面,要推大半个小时才可以推出来

Code

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<time.h>
#include<stdlib.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<string>
#include<queue>
#include<stack>
#include<set>
#include<map>
using namespace std;
typedef double db;
typedef unsigned ud;
typedef long long ll;
typedef unsigned long long ull;
typedef vector<int> vec;
typedef pair<int,int> pii;
typedef vector<pii> vecp;
#define pb push_back
#define ph push
#define fi first
#define se second
const int INF=(ud)-1>>1;
const ll inf=(ull)-1ll>>1;
template<class T>void rd(T &a){
    a=0;char c;
    while(c=getchar(),!isdigit(c));
    do a=a*10+(c^48);
        while(c=getchar(),isdigit(c));
}
template<class T>void nt(T x){
    if(!x)return;
    nt(x/10);
    putchar(48+x%10);
}
template<class T>void pt(T x){
    if(!x)putchar('0');
    else nt(x);
}
template<class T>void Max(T &a,T b){
    if(a<b)a=b;
}
template<class T>void Min(T &a,T b){
    if(a==-1||a>b)a=b;
}
const int M=1e6+5;
int prime[M],t;
bool mark[M];
inline bool chk(int x){
    int cnt=0;
    for(int i=0;prime[i]*prime[i]<=x;++i){
        if(x%prime[i]==0){
            x/=prime[i];
            if(x%prime[i]==0)return 0;
        }
    }
    return 1;
}
const int P=1e9+7;
int pw[M],phi[M],s[M];
inline void Mod_add(int &a,int b){
    if((a+=b)>=P)a-=P;
}
inline int Mod(int a){
    if(a>=P)return a-P;
    if(a<0)return a+P;
    return a;
}
inline int Mod_pow(int x,int a){
    int res=1;
    for(int i=0;1ll<<i<=a;++i){
        if(a&1<<i)res=1ll*res*x%P;
        x=1ll*x*x%P;
    }
    return res;
}
int x,n;
inline int calc(int a,int b){
    int w=Mod(Mod_pow(x,b-a+1)-1);
    int s=Mod_pow(x-1,P-2);
    int k=Mod_pow(x,a);
    return (1ll*k*w%P*s-(b-a+1))%P;
}
inline void gao(){
    cin>>x>>n;
    if(x==1){puts("0");return;}
    int res=0;
    for(int d=1;d<=n;++d){
        int last=(n/(n/d));
        Mod_add(res,1ll*calc(d,last)*s[n/d]%P);
        d=last;
    }
    cout<<res<<endl;
}
inline void Euler(){
    phi[1]=1;
    for(int i=2;i<M;++i){
        if(!mark[i])prime[t++]=i,phi[i]=i-1;
        for(int j=0,p;j<t&&(p=prime[j])*i<M;++j){
            mark[p*i]=1;
            if(i%p==0){phi[p*i]=p*phi[i];break;}
            phi[p*i]=phi[i]*(p-1);
        }
    }
    s[0]=-1;
    for(int i=1;i<M;++i)
        s[i]=Mod(s[i-1]+(phi[i]<<1));
}
int main(){
    Euler();
    int _;
    for(cin>>_;_--;)gao();
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值