为了改变数论只会GCD的尴尬局面,我们来开一波数论:
数论函数:
- 数论函数是定义域在正整数的函数。
- 积性函数: f(ab)=f(a)f(b),gcd(a,b)=1 ,完全积性函数: f(ab)=f(a)f(b) 。
- 常见积性函数: φ(n) ,μ(n) (莫比乌斯函数), d(n) (因子个数), σ(n) (因子和)。
- 单位函数 : e(n)=[n=1] 。
- 常见完全积性函数: Idk(n)=n^k , 1(n)=Id0(n) , Id(n)=Id1(n) 。
我们 有以下令人窒息的操作:
(F*G)(x)=∑d|n F(d)*G(n/d)
这种操作我们称之为狄雷克卷积。满足交换律,结合律,分配律以及 f∗e=f 。
我们有: d(n)=(1∗1)(n)
σ(n)=(Id∗1)(n)
e=μ∗1
都可以由积性函数的性质再套一个乘法分配率可以证出来。
我们有两个重要的数论函数 φ(n) ,μ(n) 。
φ(n),1到n里面与N互质的数的个数。有重要的等式:φ*1=id
μ(n), 我们可以理解为1的逆元。 我们发现狄雷克卷积可以构成一个群,生成元是e ,1* μ=e,我们又知道*1又叫求该函数的狄雷克前缀和,那么再卷上μ就是原来的函数了。
我们设F=f*1,则 f=F* μ
那么我们就可以做一些简单的题目了:
bzoj 3884 .这道题我们要一个结论:
X^i=X^(i>φ(p)?i mod φ(p)+φ(p):i) (mod p)
特殊的 ,我们有 X^i=X^(i%(p-1)) (mod p) p为质数。
我们还要知道φ(φ(p))<n/2,那么我们不妨迭代指数,最多迭代log p次指数为1,我们停止迭代。

#include<bits/stdc++.h>
using namespace std;
int sed(int &p){
int ll=0;
while (p%2==0) ll++,p>>=1; return ll;
}
int fai(int x){
int i,re=x;
for(i=2;i*i<=x;i++)
if(x%i==0)
{
re/=i;re*=i-1;
while(x%i==0)
x/=i;
}
if(x^1) re/=x,re*=x-1;
return re;
}
long long QP(long long x,int y,int p){
long long re=1;
while(y)
{
if(y&1) (re*=x)%=p;
(x*=x)%=p; y>>=1;
}
return re;
}
long long dos(int p){
if (p==1) return 1;
int k=sed(p), fi=fai(p);
long long re=dos(fi);
(re+=fi-k%fi)%=fi;
re=QP(2,re,p)%p;
return (re<<k);
}
int main () {
int t,p;
scanf("%d",&t);
while (t--) {
scanf("%d",&p);
printf("%lld\n",dos(p)%p);
}
}
bzoj 2186
这某非就是传说中的gcd?

#include<bits/stdc++.h>
#define inff 10000000
#define N 10000090
int n,m,p,tot,tops,t,T;
int ans[N];int fa[N];
bool f[N];char c; int b,inf;
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x) {
c=nc();
b=1;
for (; !(c>='0' && c<='9'); c=nc()) if (c=='-') b=-1;
for (x=0; c>='0' && c<='9'; x=x*10+c-'0',c=nc());
x*=b;
}
inline void exgcd(int a,int b,int &x,int &y)
{
if (!b){x=1;y=0;return;}
exgcd(b,a%b,x,y);
int t=x;x=y;y=t-a/b*x;
}
inline int getinv(int a)
{
int x=0,y=0;
exgcd(a,p,x,y);
return (x%p+p)%p;
}
void getfine(){
ans[1]=1;t=0;
for (int i=2;i<=inf;i++)
{
if (!f[i])
ans[i]=(long long)1ll*ans[i-1]*(i-1)%p*getinv(i)%p,fa[++t]=i;
else ans[i]=ans[i-1];
for(int j=1;j<=t;j++)
{
if((long long)fa[j]*i>inf)break;
f[fa[j]*i]=1;
if(i%fa[j]==0)break;
}
}
}
inline void write(int x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
int main (){
//scanf("%d%d",&T,&p);
read(T); read(p);
inf=min(p,inff);
getfine();fa[0]=1;
for (int i=1;i<=inf;i++) fa[i]=(long long)(1ll*fa[i-1]*i)%p;
while (T--){
//scanf("%d%d",&n,&m);
read(n); read(m);
int top=1ll*fa[n]%p*ans[m]%p;
//printf("%d\n",top);
write(top); putchar('\n');
}
}
bzoj 2440
二分答案,我们转为一个计数问题,我们统计有多少符合条件的数小于X?
由容斥原理得 ans=∑ x/(i*i)*μ(i)

#include<bits/stdc++.h>
#define N 50000
#define ll long long
using namespace std;
inline ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int T;
ll ans,K;
int tot;
int mu[50005],pri[50005];
bool mark[50005];
void getmu()
{
mu[1]=1;
for(int i=2;i<=N;i++)
{
if(!mark[i])pri[++tot]=i,mu[i]=-1;
for(int j=1;j<=tot&&pri[j]*i<=N;j++)
{
mark[i*pri[j]]=1;
if(i%pri[j]==0){mu[i*pri[j]]=0;break;}
else mu[i*pri[j]]=-mu[i];
}
}
}
ll cal(int x)
{
ll sum=0;
int t=sqrt(x);
for(int i=1;i<=t;i++)
sum+=x/(i*i)*mu[i];
return sum;
}
int main()
{
getmu();
T=read();
while(T--)
{
K=read();
ll l=K,r=1644934081;
while(l<=r)
{
ll mid=(l+r)>>1;
if(cal(mid)>=K)ans=mid,r=mid-1;
else l=mid+1;
}
printf("%lld\n",ans);
}
return 0;
}
给出T组N和M,依次求出
的值
我们考虑反演:∑i∑j (gcd=1) =∑i∑j ∑d |gcd(i,j) μ(i)=∑ d μ(d) (n/d)*(m/d)
我们发现其只有O(N^0.5)个整点,分块处理。

#include<bits/stdc++.h>
#define N 1000000
bool mark[N+5];
int prime[N+5];
int num;
long long sum[N+5];
int euler[N+5];
inline int Min(int a,int b){return a<b?a:b;}
void Euler()
{
int i,j;
num = 0;
memset(euler,0,sizeof(euler));
memset(prime,0,sizeof(prime));
memset(mark,0,sizeof(mark));
euler[1] = 1; // multiply function
sum[1]=1;
for(i=2;i<=N;i++)
{
if(!mark[i])
{
prime[num++] = i;
euler[i] = -1;
}
for(j=0;j<num;j++)
{
if(i*prime[j]>N){break;}
mark[i*prime[j]] = 1;
if(i%prime[j]==0)
{
euler[i*prime[j]] = 0;
break;
}
else
{
euler[i*prime[j]] = -euler[i];
}
} sum[i]=sum[i-1]+euler[i];
}
}
int main()
{
int i;
int M1,M2;
Euler(); int t;
scanf("%d",&t);
while (t--){
scanf("%d%d",&M1,&M2);
long long ans = 0;
int min = Min(M1,M2);
/*for(i=1;i<=min;i++)
{
sum += euler[i]*(M1/i)*(M2/i);
}*/
for (int i=1,last;i<=min;i=last+1){
last=Min(M1/(M1/i),M2/(M2/i));
ans+=(sum[last]-sum[i-1])*(M1/i)*(M2/i);
}
printf("%lld\n",ans);
}
}
给出T组N和M,求
的值

我们考虑反演:∑i∑j (gcd=1) =∑i∑j ∑d |gcd(i,j)φ (i)=∑ dφ(d) (n/d)*(m/d)
我们发现其只有O(N^0.5)个整点,分块处理。

#include<bits/stdc++.h>
#define N 1000000
bool mark[N+5];
int prime[N+5];
int num;
long long sum[N+5];
int euler[N+5];
inline int Min(int a,int b){return a<b?a:b;}
void Euler()
{
int i,j;
num = 0;
memset(euler,0,sizeof(euler));
memset(prime,0,sizeof(prime));
memset(mark,0,sizeof(mark));
euler[1] = 1; // multiply function
sum[1]=1;
for(i=2;i<=N;i++)
{
if(!mark[i])
{
prime[num++] = i;
euler[i] = i-1;
}
for(j=0;j<num;j++)
{
if(i*prime[j]>N){break;}
mark[i*prime[j]] = 1;
if(i%prime[j]==0)
{
euler[i*prime[j]] = euler[i]*prime[j];
break;
}
else
{
euler[i*prime[j]] = euler[i]*euler[prime[j]];
}
} sum[i]=sum[i-1]+euler[i];
}
}
int main()
{
int i;
int M1,M2;
Euler(); int t;
scanf("%d",&t);
while (t--){
scanf("%d%d",&M1,&M2);
long long ans = 0;
int min = Min(M1,M2);
/*for(i=1;i<=min;i++)
{
sum += euler[i]*(M1/i)*(M2/i);
}*/
for (int i=1,last;i<=min;i=last+1){
last=Min(M1/(M1/i),M2/(M2/i));
ans+=(long long)1ll*(sum[last]-sum[i-1])*(M1/i)*(M2/i);
}
printf("%lld\n",ans);
}
}
那么我们就可以上杜教筛了。
举个例子:这里 我们要在线性以下的时间搞定φ的前缀和。
(⊙o⊙)… 我放弃了,不会打数学公式。

#include<bits/stdc++.h>
#define N 4011007
#define LL long long
#define mo 1000000007
#define fg
#define int LL
using namespace std;
LL pim[N>>3],fi[N],tot,a[N],b[N],inv2=500000004,s=N-1007,n,SI,fis[N];
inline LL Mo(LL x){x%=mo;if (x<0) x+=mo;return x;}
LL tr(LL x) {if (x<=s) return a[x]; return b[n/x];}
void put(LL x,LL y) {if (x<=s) a[x]=y; else b[n/x]=y;}
LL F(LL x) {
if (x<SI) return fis[x];
if (tr(x)) return tr(x);
LL ans=Mo(x)*Mo(x+1)%mo*inv2%mo;
for (LL i=2,last;i<=x;i=last+1) {
last=x/(x/i);
ans=Mo(ans-F(x/i)*Mo(last-i+1)%mo);
} put(x,ans);return ans;
}
void Pin(int MA){
fis[1]=1;
for (int i=2;i<MA;i++) {
if (!fi[i]) pim[++tot]=i,fi[i]=i-1;
for (int j=1;j<=tot&&pim[j]*i<MA;j++)
if(i%pim[j]) fi[i*pim[j]]=fi[i]*(pim[j]-1);
else {fi[i*pim[j]]=fi[i]*pim[j]; break;}
fis[i]=Mo(fis[i-1]+fi[i]);
}
}
void Init() {
SI=min((int)powl(n,0.666),(LL)N-12);
Pin(SI);
}
signed main () {
scanf("%lld",&n);
Init();
printf("%lld",F(n));
}
再丢几道题目:

#include<bits/stdc++.h>
#define LL long long
#define N 4000007
using namespace std;
map<LL,int> ma;
LL a,n;
int LS,fis[N],pim[N>>3],tot,fi[N],u[N];
bool p[N];
LL F(LL x){
if (x<LS) return fis[x];
if (ma.count(x)) return ma[x];
LL ans=1;
for (LL i=2,last;i<=x;i=last+1) {
last=x/(x/i); ans=ans-(last-i+1)*F(x/i);
} ma[x]=ans; return ans;
}
void LST(int LS){
fis[1]=1;
for (int i=2;i<LS;i++) {
if (!p[i]) u[i]=-1,pim[++tot]=i;
for (int j=1;j<=tot&&i*pim[j]<LS;j++) {
p[i*pim[j]]=1; if (i%pim[j]) u[i*pim[j]]=-u[i]; else{ u[i*pim[j]]=0; break;}
}
fis[i]=(fis[i-1]+u[i]);
}
}
void init() {
LS=min((int)pow(n,0.6667),N-47);
LST(LS);
}
int main () {
scanf("%lld %lld",&a,&n);
init();
printf("%lld",F(n)-F(a-1));
}

#include<bits/stdc++.h>
#define mo 1000000007
#define N 5000007
#define LL long long
using namespace std;
LL n,fis[N],pim[N>>3],fi[N],IS,inv2=500000004,tot,ans,a[N],b[N],s=N-1007;
inline LL Mo(LL x){x%=mo; if (x<0) x+=mo; return x;}
//map<LL,LL> Ma;
LL tr(LL x) {if (x<=s) return a[x]; return b[n/x];}
void put(LL x,LL y) {if (x<=s) a[x]=y; else b[n/x]=y;}
LL fiz(LL x){
if (x<=IS) return fis[x];
if (tr(x)) return tr(x);
// printf("%d\n",x);
LL ans=x%mo*((x+1)%mo)%mo*inv2%mo;
for (LL i=2,last;i<=x;i=last+1) {
last=x/(x/i);
ans=(ans-fiz(x/i)*
((last-i+1)%mo)%mo)%mo;
}
put(x,ans); return ans;
}
void P_pit(LL ma_l){
fis[1]=1;
for (int i=2;i<=ma_l;i++) {
if (!fi[i]) pim[++tot]=i,fi[i]=i-1;
for (int j=1;j<=tot&&i*pim[j]<=ma_l;j++)
if (i%pim[j]) fi[i*pim[j]]=fi[i]*(pim[j]-1);
else {fi[i*pim[j]]=fi[i]*pim[j];break;}
fis[i]=fi[i]+fis[i-1];
}
}
void init() {
IS=min(N-17,(int)powl(n,0.6666));
P_pit(IS);
fiz(n);
}
int main () {
// freopen("a.in","r",stdin);
scanf("%lld",&n);
init();
for (LL i=1,last;i<=n;i=last+1) {
last=n/(n/i);
ans=(ans+Mo(n/i)*Mo(n/i)*Mo(fiz(last)-fiz(i-1))%mo);
ans=Mo(ans);
}
printf("%lld\n",ans); return 0;
}
来一发技巧。对于单点杜教筛的话,我们可以这样存取值
LL tr(LL x) {if (x<=s) return a[x]; return b[n/x];}
void put(LL x,LL y) {if (x<=s) a[x]=y; else b[n/x]=y;}
那么我们就不用map 或哈希了。
以后再学洲阁筛吧,挖坑就跑。
来源:https://www.cnblogs.com/rrsb/p/8321489.html
