一些数论函数题

luogu2257 YY的GCD

答案为:
\(\sum\limits_{p\in prime}\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[gcd(i,j)==p]\)
\(=\sum\limits_{p\in prime}\sum\limits_{i=1}^{\lfloor\frac{n}{p}\rfloor}\sum\limits_{j=1}^{{\lfloor\frac{m}{p}}\rfloor}[gcd(i,j)==1]\)
\(=\sum\limits_{p\in prime}\sum\limits_{i=1}^{\lfloor\frac{n}{p}\rfloor}\sum\limits_{j=1}^{{\lfloor\frac{m}{p}}\rfloor}\sum\limits_{t \mid i\land t\mid j}\mu(t)\)
\(=\sum\limits_{p\in prime}\sum\limits_{t=1}^{min(n,m)}\mu(t)\sum\limits_{i=1}^{\lfloor\frac{n}{pt}\rfloor}\sum\limits_{j=1}^{{\lfloor\frac{m}{pt}}\rfloor}1\)
\(=\sum\limits_{p\in prime}\sum\limits_{t=1}^{min(n,m)}\mu(t)\lfloor\frac{n}{pt}\rfloor\lfloor\frac{m}{pt}\rfloor\)
令\(T=pt\)。
原式\(=\sum\limits_{p\in prime}\sum\limits_{t=1}^{min(n,m)}\mu(t)\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor\)
\(=\sum\limits_{T=1}^{min(n,m)}\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor\sum\limits_{p\in prime \land p\mid T}\mu(\frac{T}{p})\)
前面的\(\sum\limits_{T=1}^{min(n,m)}\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor\)就是一个整除分块。这就需要我们求出\(\sum\limits_{p\in prime \land p\mid T}\mu(\frac{T}{p})\)的前缀合,我们枚举p然后遍历值域就是调和级数复杂度的预处理。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int mu[10000002],prim[1000000],vis[10000002],g[10000002],sum[10000002],cnt,n[10100],m[10100],t;
long long ans;
inline void read(int &x)
{
    x=0;
    static int p;p=1;
    static char c;c=getchar();
    while(!isdigit(c)){if(c=='-')p=-1;c=getchar();}
    while(isdigit(c)) {x=(x<<1)+(x<<3)+(c-48);c=getchar();}
    x*=p;
}
void getmu(int n){
    mu[1]=1;
    for(int i=2;i<=n;i++){
        if(!vis[i]){
            prim[++cnt]=i;
            mu[i]=-1;
        }
        for(int j=1;i*prim[j]<=n;j++){
            vis[i*prim[j]]=1;
            if(i%prim[j]==0)break;
            else mu[i*prim[j]]=-mu[i];
        }
    }
    for(int i=1;i<=cnt;i++)
        for(int j=1;j*prim[i]<=n;j++)
            g[j*prim[i]]+=mu[j];
    for(int i=1;i<=n;i++)
        sum[i]=sum[i-1]+g[i];
}
int main(){
    read(t);
    int tot=0;
    for(int i=1;i<=t;i++){
        read(n[i]);read(m[i]);
        tot=max(tot,max(n[i],m[i]));
    }
    getmu(tot);
    for(int i=1;i<=t;i++){
        ans=0;
        if(n[i]>m[i])swap(n[i],m[i]);
        for(int l=1,r;l<=n[i];l=r+1){
            r=min(n[i]/(n[i]/l),m[i]/(m[i]/l));
            ans+=(long long)(n[i]/l)*(m[i]/l)*(sum[r]-sum[l-1]);
        }
        printf("%lld\n",ans);
    }
    return 0;
} 

[CQOI2015]选数

显然是一道莫比乌斯反演。
设f[i]为选出n个数gcd为i的方案数。
设F[i]为选出n个数gcd为i的倍数的方案数。
把L,H都除去k,答案就是f[1](注意一点除的时候当L%k!=0是L除完k之后还要加1)。
然后有\(F[i]=\sum\limits_{i \mid d }f[d]\)
\(f[i]=\sum\limits_{i \mid d} \mu(\frac{d}{i})F[d]\)
应为我们要求的是f[1],
\(f[1]=\sum\limits_d \mu(d)F[d]\)
F怎么求就是可以选的数的n次方:\((\lfloor\frac{H}{k}\rfloor-\lfloor\frac{L-1}{k}\rfloor)^n\)
\(\mu\)前缀和用杜教筛就行了。

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdio>
#include<map>
using namespace std;
#define int long long
map<int,bool>vis;
map<int,int> hsh;
const int N=2001000;
const int p=1e9+7;
int mu[N],book[N],prime[N],cnt,n,k,L,H;
int read(){
    int sum=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    return sum*f;
}
void pre_work(){
    mu[1]=1;
    for(int i=2;i<=1000000;i++){
        if(book[i]==0){
            prime[++cnt]=i;
            mu[i]=-1;
        }
        for(int j=1;j<=cnt&&prime[j]*i<=1000000;j++){
            book[i*prime[j]]=1;
            if(i%prime[j]==0)break;
            mu[i*prime[j]]=-mu[i];
        }
    }
    for(int i=1;i<=1000000;i++)mu[i]+=mu[i-1];
}
int sum(int x){
    if(x<=1000000)return mu[x];
    if(vis[x]==1)return hsh[x];
    int tmp=1;
    for(int l=2,r;l<=x;l=r+1){
        r=(x/(x/l));
        tmp=tmp-(r-l+1ll)*sum(x/l)%p;
        tmp=(tmp%p+p)%p;
    }
    vis[x]=1;hsh[x]=tmp;
    return tmp;
}
int ksm(int x,int b){
    int tmp=1;
    while(b){
        if(b&1)tmp=tmp*x%p;
        b>>=1;
        x=x*x%p;
    }
    return tmp;
}
int work(){
    int tmp=0;
    for(int l=1,r;l<=H;l=r+1){
        if(l>L-1ll)r=H/(H/l);
        else r=min(H/(H/l),(L-1ll)/((L-1ll)/l));
        tmp=tmp+(sum(r)-sum(l-1ll))*ksm((H/l-(L-1ll)/l),n)%p;
        tmp=(tmp%p+p)%p;
    }
    return tmp;
}
signed main(){
    pre_work();
    n=read();k=read();L=read();H=read();
    if(L%k)L=L/k+1;
    else L=L/k;
    H=H/k;
    printf("%lld",work());
    return 0;
}

BZOJ 4176 Lucas的数论


\((n \leq 1e9)\)f代表约数个数。
\(f(ij)=\sum\limits_{x \mid i }\sum\limits_{ y\mid j}[gcd(x,y)==1]\)
我有一个精巧的证明可惜这里地方太小,写不下。
(我才知道这是费马说的,思维风暴证明费马大定理?)
我才没有什么精巧的证明,我会暴力。
因为约束个数函数是极性函数,把每一个质因子分开考虑。
设ij中有\(p^k\),i中有\(p^a\),j中有\(p^b\),其中a+b=k。
然后我们惊奇的发现\(p^k\)的约数个数为k+1=a+b+1。然后\(p^a\),和\(p^b\)互质的情况有a+b+1。
然后就可以嘿嘿嘿了。
原式等于:\(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\sum\limits_{x \mid i }\sum\limits_{ y\mid j}[gcd(x,y)==1]\)
\(=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\sum\limits_{x \mid i }\sum\limits_{y\mid j}\sum\limits_{t\mid x\land t\mid y}\mu(t)\)
\(=\sum\limits_{t=1}^{n}\mu(t)\sum\limits_{x=1}^{\lfloor\frac{n}{t}\rfloor}\lfloor\frac{n}{tx}\rfloor\sum\limits_{y=1}^{\lfloor\frac{n}{t}\rfloor}\lfloor\frac{n}{ty}\rfloor\)
\(=\sum\limits_{t=1}^{n}\mu(t) (\sum\limits_{x=1}^{ \lfloor \frac{n}{t} \rfloor} \lfloor\frac{n}{tx}\rfloor)^2\)
然后上整除分块。
复杂度。。听说是\(O(\sqrt{n}log\sqrt{n})\)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<map>
using namespace std;
#define int long long
const int p=1e9+7;
const int N=1010000;
map<int,int>hsh;
map<int,bool>vis;
int mu[N],book[N],prime[N],cnt,n;
void pre_work(){
    mu[1]=1;
    for(int i=2;i<=1000000;i++){
        if(book[i]==0){
            prime[++cnt]=i;
            mu[i]=-1;
        }
        for(int j=1;j<=cnt&&prime[j]*i<=1000000;j++){
            book[i*prime[j]]=1;
            if(i%prime[j]==0)break;
            mu[i*prime[j]]=-mu[i];
        }
    }
    for(int i=1;i<=1000000;i++)mu[i]+=mu[i-1];
}
int work(int x){
    int tmp=0;
    for(int l=1,r;l<=x;l=r+1){
        r=x/(x/l);
        tmp=(tmp+(r-l+1)*(x/l)%p)%p;
    }
    return tmp;
}
int sum(int x){
    if(x<=1000000)return mu[x];
    if(vis[x])return hsh[x];
    int tmp=1;
    for(int l=2,r;l<=x;l=r+1){
        r=x/(x/l);
        tmp=tmp-sum(x/l)*(r-l+1);
        tmp=(tmp%p+p)%p;
    }
    vis[x]=1;hsh[x]=tmp;
    return tmp;
}
int calc(int x){
    int tmp=0;
    for(int l=1,r;l<=x;l=r+1){
        r=x/(x/l);
        int w=work(x/l);
        tmp=tmp+(sum(r)-sum(l-1))*w%p*w%p;
        tmp=(tmp%p+p)%p;
    }
    return tmp;
}
int read(){
    int sum=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    return sum*f;
}
signed main(){
    n=read();
    pre_work();
    printf("%lld",calc(n));
    return 0;
}

luogu P3768 简单的数学题

\(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}ijgcd(i,j)\)
gcd不仅可以化成\(\mu\)
原式等于
\(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}ij\sum\limits_{t \mid i \land t \mid j} \varphi(t)\)
为什么?
因为\(\varphi*1\)=id
然后接着化简。
\(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}ij\sum\limits_{t \mid i \land t \mid j} \varphi(t)\)
\(=\sum\limits_{t=1}^{n}\varphi(t)*t^2\sum\limits_{i=1}^{\lfloor\frac{n}{t}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{n}{t}\rfloor}ij\)
\(=\sum\limits_{t=1}^{n}\varphi(t)*t^2(\sum\limits_{i=1}^{\lfloor\frac{n}{t}\rfloor}i)^2\)
\(=\sum\limits_{t=1}^{n}\varphi(t)*t^2\sum\limits_{i=1}^{\lfloor\frac{n}{t}\rfloor}i^3\)
然后就可以做了。
最后一步怎么化简?暴力展开。。。
然后上杜教筛就行了。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<map>
using namespace std;
#define int long long
const int p=1e9+7;
const int N=1010000;
map<int,int>hsh;
map<int,bool>vis;
int mu[N],book[N],prime[N],cnt,n;
void pre_work(){
    mu[1]=1;
    for(int i=2;i<=1000000;i++){
        if(book[i]==0){
            prime[++cnt]=i;
            mu[i]=-1;
        }
        for(int j=1;j<=cnt&&prime[j]*i<=1000000;j++){
            book[i*prime[j]]=1;
            if(i%prime[j]==0)break;
            mu[i*prime[j]]=-mu[i];
        }
    }
    for(int i=1;i<=1000000;i++)mu[i]+=mu[i-1];
}
int work(int x){
    int tmp=0;
    for(int l=1,r;l<=x;l=r+1){
        r=x/(x/l);
        tmp=(tmp+(r-l+1)*(x/l)%p)%p;
    }
    return tmp;
}
int sum(int x){
    if(x<=1000000)return mu[x];
    if(vis[x])return hsh[x];
    int tmp=1;
    for(int l=2,r;l<=x;l=r+1){
        r=x/(x/l);
        tmp=tmp-sum(x/l)*(r-l+1);
        tmp=(tmp%p+p)%p;
    }
    vis[x]=1;hsh[x]=tmp;
    return tmp;
}
int calc(int x){
    int tmp=0;
    for(int l=1,r;l<=x;l=r+1){
        r=x/(x/l);
        int w=work(x/l);
        tmp=tmp+(sum(r)-sum(l-1))*w%p*w%p;
        tmp=(tmp%p+p)%p;
    }
    return tmp;
}
int read(){
    int sum=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    return sum*f;
}
signed main(){
    n=read();
    pre_work();
    printf("%lld",calc(n));
    return 0;
}

[SDOI2017]数字表格

\(\Pi_{i=1}^{n}\Pi_{j=1}^{m}f[gcd(i,j)]\)

\(=\Pi_{d=1} f[d]^{\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{n}{d}\rfloor}[gcd(i,j)==1]}\)
\(=\Pi_{d=1} f[d]^{\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{t \mid i\land t\mid j}\mu(t)}\)
\(=\Pi_{d=1} f[d]^{\sum\limits_{i=1}^{min(n,m)}\mu(t)\lfloor\frac{n}{dt}\rfloor\lfloor\frac{m}{dt}\rfloor}\)
令T=dt
\(=\Pi_{T=1}^{min(m,n)}\Pi_{d\mid T} f[d]^{\mu(\frac{T}{d})\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor}\)
然后我们把\(\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor\)分块。\(\Pi_{d\mid T} f[d]^{\mu(\frac{T}{d})}\)预处理。因为调和级数复杂度可以接受。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define int long long
const int mod=1e9+7;
const int N=1010100;
int mu[N],book[N],prime[N],cnt,f[N],T,n,m,F[N],inv[N];
int read(){
    int sum=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    return sum*f;
}
int ksm(int x,int b){
    int tmp=1;
    while(b){
        if(b&1)tmp=tmp*x%mod;
        b>>=1;
        x=x*x%mod;
    }
    return tmp;
}
void pre_work(){
    mu[1]=1;
    for(int i=2;i<=1000000;i++){
        if(book[i]==0){
            mu[i]=-1;
            prime[++cnt]=i;
        }
        for(int j=1;j<=cnt&&i*prime[j]<=1000000;j++){
            book[i*prime[j]]=1;
            if(i%prime[j]==0)break;
            mu[i*prime[j]]=-mu[i];
        }
    }
    f[0]=0;f[1]=1;
    for(int i=2;i<=1000000;i++)
        f[i]=(f[i-1]+f[i-2])%mod;
    for(int i=0;i<=1000000;i++)F[i]=1;
    for(int i=1;i<=1000000;i++)
        for(int j=1;j*i<=1000000;j++){
            int tmp;
            if(mu[j]==0)tmp=1;
            else if(mu[j]==1)tmp=f[i];
            else tmp=ksm(f[i],mod-2);
            F[i*j]=(F[i*j]*tmp)%mod;
        }
    for(int i=2;i<=1000000;i++)F[i]=(F[i-1]*F[i])%mod;
    for(int i=1;i<=1000000;i++)inv[i]=ksm(F[i],mod-2);
    inv[0]=1;
}
int work(int n,int m){
    if(n>m)swap(n,m);
    int tmp=1;
    for(int l=1,r;l<=n;l=r+1){
        r=min(m/(m/l),n/(n/l));
        tmp=(tmp*ksm(F[r]*inv[l-1]%mod,(n/l)*(m/l)))%mod;
    }
    return tmp;
}
signed main(){
    T=read();
    pre_work();
    while(T--){
        n=read(),m=read();
        printf("%lld\n",work(n,m));
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Xu-daxia/p/10262443.html

时间: 2024-10-12 01:30:19

一些数论函数题的相关文章

hdu6706 huntian oy

hdu 好久没写数论函数题了,上一次写还是在纪中学min25筛的时候了,赶紧来一道补下手感 题面:求 \[ \sum_{i=1}^n\sum_{j=1}^igcd(i^a-j^a,i^b-j^b)[gcd(i,j)=1] \] 保证\(n,a,b\leq 10^9,gcd(a,b)=1\) 知道\((i^a-j^a)\)这个玩意因式分解后有\((i-j)\),不妨大力猜一下\(gcd(i^a-j^a,i^b-j^b)=i-j\),证明明天再补 那么\[ \begin{aligned} 原式=&\

C#认证第一章1 题 11题

C#第一章第一题 C#认证第一章  11题

[poj2104]可持久化线段树入门题(主席树)

解题关键:离线求区间第k小,主席树的经典裸题: 对主席树的理解:主席树维护的是一段序列中某个数字出现的次数,所以需要预先离散化,最好使用vector的erase和unique函数,很方便:如果求整段序列的第k小,我们会想到离散化二分和线段树的做法, 而主席树只是保存了序列的前缀和,排序之后,对序列的前缀分别做线段树,具有差分的性质,因此可以求任意区间的第k小,如果主席树维护索引,只需要求出某个数字在主席树中的位置,即为sort之后v中的索引:若要求第k大,建树时反向排序即可 1 #include

旧题新做:从idy的视角看数据结构

“今天你不写总结……!!!” 额…… 还是讲我的吧.这些考试都是idy出的题. 20170121:DFS序. ST表.线段树练习 这是第一次考数据结构. Problem 1. setsum 1 second 给你一个长度为N 的整数序列,支持两种操作: • modity l r val 将区间[l,r] 中的所有数修改为val • query l r 询问区间[l,r] 所有数的和 分析:最简单的线段树,区间更改区间求和.但注意是更改,不是添改,sum与flag需同时覆盖. Problem 2.

做预解释题的一点小方法和小技巧

在JavaScript中的函数理解中预解释是一个比较难懂的话题.原理虽然简单,寥寥数言,但其内涵却有深意,精髓难懂.如何在轻松活跃的头脑中将它学会,现在针对我在学习中的一点小窍门给大家分享一下,希望能给大家一些帮助: 万事需遵循"原理"--"预解释"无节操和"this"指向:(可先看例题解析然后结合原理进行学习) (感谢蕾蕾老师给归纳的预解释无节操原理:) 如果函数传参数则先于以下执行,就相当于在函数私有作用域下var了一个变量:根据作用域原理,

hdu 2966 In case of failure kdtree模板题

问求每个点距离平方的最小的点 kd-tree模板题…… 1 #include<bits/stdc++.h> 2 #define cl(a,b) memset(a,b,sizeof(a)) 3 #define debug(x) cerr<<#x<<"=="<<(x)<<endl 4 using namespace std; 5 typedef long long ll; 6 typedef pair<int,int>

2017 计蒜之道 初赛 第一场 A、B题

A题 阿里的新游戏 题目概述: 阿里九游开放平台近日上架了一款新的益智类游戏--成三棋.成三棋是我国非常古老的一个双人棋类游戏,其棋盘如下图所示: 成三棋的棋盘上有很多条线段,只能在线段交叉点上放入棋子.我们可以用坐标系来描述棋盘: 如果一条线段上的三个交叉点都被同一玩家的棋子占据的话,则称这条线段被该玩家 成三.现在,小红和小明两人在游戏平台上下棋,其中小红的棋子是黑色的.请你帮小红计算他成三的线段数. 样例对应的棋盘如下: 输入格式 输入第一行两个整数 n,m(3 \le n, m \le

老男孩教育每日一题-2017年5月11-基础知识点: linux系统中监听端口概念是什么?

1.题目 老男孩教育每日一题-2017年5月11-基础知识点:linux系统中监听端口概念是什么? 2.参考答案 监听端口的概念涉及到网络概念与TCP状态集转化概念,可能比较复杂不便理解,可以按照下图简单进行理解? 将整个服务器操作系统比喻作为一个别墅 服务器上的每一个网卡比作是别墅中每间房间 服务器网卡上配置的IP地址比喻作为房间中每个人 而房间里面人的耳朵就好比是监听的端口 当默认采用监听0.0.0.0地址时,表示房间中的每个人都竖起耳朵等待别墅外面的人呼唤当别墅外面的用户向房间1的人呼喊时

POJ 1741 Tree(树的点分治,入门题)

Tree Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 21357   Accepted: 7006 Description Give a tree with n vertices,each edge has a length(positive integer less than 1001).Define dist(u,v)=The min distance between node u and v.Give an in