[bzoj5332][bzoj5276][bzoj3994][莫比乌斯反演][三元环计数]旧试题/skyfall/约数个数和

63 篇文章 0 订阅
14 篇文章 0 订阅

Description

旧试题
skyfall
约数个数和

约数个数和题解

由于这三个题长得比较像就放一起吧
D ( u ) D(u) D(u)表示 u u u的约数和,那么有如下结论
D ( x ∗ y ) = ∑ u ∣ x ∑ v ∣ y [ g c d ( u , v ) = = 1 ] D(x*y)=\sum_{u|x}\sum_{v|y} [gcd(u,v)==1] D(xy)=uxvy[gcd(u,v)==1]
证明如下,设 k i k_i ki表示质因数 p i p_i pi在某个数中的次数
显然约数个数和为 Π ( k i + 1 ) \Pi(k_i+1) Π(ki+1)
显然可以知道, x ∗ y x*y xy中的某个 k i k_i ki一定是在 x x x中贡献了 u i u_i ui,在 y y y中贡献了 v i v_i vi并且满足 u i + v i = k + i u_i+v_i=k+i ui+vi=k+i的。那么对于上式,由于 g c d ( u , v ) = 1 gcd(u,v)=1 gcd(u,v)=1,故在 x ∗ y x*y xy的某个质因数 p i p_i pi中,仅存在三种情况
该质因数被 u u u取走,有 u i u_i ui种取的次数,该质因数被 v v v取走,有 v i v_i vi种取的次数,或者两者均不取,有 1 1 1的次数,则该质因数的合法取法有 u i + v i + 1 = k i + 1 u_i+v_i+1=k_i+1 ui+vi+1=ki+1种,相乘即为约数个数和
拓展到更高维也是一样的
那么式子可以写为
以下均视为 n ≤ m n\leq m nm
∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = = 1 ] ⌊ n i ⌋ ⌊ m j ⌋ \sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)==1]\lfloor\frac{n}{i}\rfloor\lfloor\frac{m}{j}\rfloor i=1nj=1m[gcd(i,j)==1]injm
随意反演一下就可以得到,
∑ d = 1 n μ ( d ) ∗ ∑ i = 1 ⌊ n d ⌋ ⌊ n i ∗ d ⌋ ∗ ∑ j = 1 ⌊ m d ⌋ ⌊ m j ∗ d ⌋ \sum_{d=1}^{n}\mu(d)*\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\lfloor\frac{n}{i*d}\rfloor*\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor}\lfloor\frac{m}{j*d}\rfloor d=1nμ(d)i=1dnidnj=1dmjdm
注意到有式子 ⌊ n i ∗ j ⌋ = ⌊ ⌊ n i ⌋ j ⌋ \lfloor\frac{n}{i*j}\rfloor=\lfloor\frac{\lfloor\frac{n}{i}\rfloor}{j}\rfloor ijn=jin
故上式可写为
∑ d = 1 n μ ( d ) ∗ ∑ i = 1 ⌊ n d ⌋ ⌊ ⌊ n d ⌋ i ⌋ ∗ ∑ j = 1 ⌊ m d ⌋ ⌊ m d ⌋ i ⌋ \sum_{d=1}^{n}\mu(d)*\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\lfloor\frac{\lfloor\frac{n}{d}\rfloor}{i}\rfloor*\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor}\frac{\lfloor\frac{m}{d}\rfloor}{i}\rfloor d=1nμ(d)i=1dnidnj=1dmidm
我写这个的时候没有及时意识到把 d d d提上去然后自闭了一会…
n n n\sqrt n nn 预处理一个 f ( i ) = ∑ j = 1 i ⌊ i j ⌋ f(i)=\sum_{j=1}^{i} \lfloor\frac{i}{j}\rfloor f(i)=j=1iji
剩下就可以 T ∗ ( n + m ) T*(\sqrt n+\sqrt m) T(n +m )

skyfall题解

与上一个题式子基本是相同的
在开头枚举一下 c c c就可以获得一个 n 2 log ⁡ n n^2\log n n2logn的做法了
式子是
∑ k = 1 c ⌊ c k ⌋ ∗ ∑ d = 1 a μ ( d ) ∗ ∑ i = 1 ⌊ a d ⌋ ⌊ a i ∗ d ⌋ ∗ [ g c d ( a d , k ) = = 1 ] ∗ ∑ j = 1 ⌊ b d ⌋ ⌊ b j ∗ d ⌋ ∗ [ g c d ( b d , k ) = = 1 ] \sum_{k=1}^{c}\lfloor \frac{c}{k}\rfloor*\sum_{d=1}^{a}\mu(d)*\sum_{i=1}^{\lfloor\frac{a}{d}\rfloor}\lfloor\frac{a}{i*d}\rfloor*[gcd(ad,k)==1]*\sum_{j=1}^{\lfloor\frac{b}{d}\rfloor}\lfloor\frac{b}{j*d}\rfloor*[gcd(bd,k)==1] k=1ckcd=1aμ(d)i=1daida[gcd(ad,k)==1]j=1dbjdb[gcd(bd,k)==1]
可以直接做了
听说需要卡常

旧试题题解

上面都太naive了啊.
这才是最终的哥哥题
同样运用上面的结论,式子化一下就可以得到
∑ d a = 1 m a x ( a , b , c ) ∑ d b = 1 m a x ( a , b , c ) ∑ d c = 1 m a x ( a , b , c ) μ ( a ) μ ( b ) μ ( c ) ∗ ∑ i ∣ l c m ( d a , d b ) ⌊ a i ⌋ ∗ ∑ j ∣ l c m ( d b , d c ) ⌊ b j ⌋ ∗ ∑ k ∣ l c m ( d a , d c ) ⌊ c k ⌋ \sum_{da=1}^{max(a,b,c)}\sum_{db=1}^{max(a,b,c)}\sum_{dc=1}^{max(a,b,c)}\mu(a)\mu(b)\mu(c)*\sum_{i|lcm(da,db)}\lfloor\frac{a}{i}\rfloor*\sum_{j|lcm(db,dc)}\lfloor\frac{b}{j}\rfloor*\sum_{k|lcm(da,dc)}\lfloor\frac{c}{k}\rfloor da=1max(a,b,c)db=1max(a,b,c)dc=1max(a,b,c)μ(a)μ(b)μ(c)ilcm(da,db)iajlcm(db,dc)jbklcm(da,dc)kc
前面是枚举约数的,用原式换过去不难得到
后面显然可以 n l o g n nlogn nlogn直接预处理出来
于是我们考虑怎么算前面合法的 a , b , c a,b,c a,b,c的贡献
首先,显然合法的 a , b , c a,b,c a,b,c需要满足任意两个数之间的 l c m ≤ max ⁡ ( a , b , c ) lcm\leq \max(a,b,c) lcmmax(a,b,c)
下文不妨设 n = m a x ( a , b , c ) n=max(a,b,c) n=max(a,b,c)
如果我们给两个合法的数对连上无向边,显然能做出贡献的就是图中的三元环
数三元环的数量是可以做到 m m m\sqrt m mm 的,其中 m m m为边数证明不会
先介绍如何做到 m m m\sqrt m mm
我们将边定向,满足任意一条边都是从度数较小的点出去,进入度数较大的点
接下来枚举所有点计数,我们发现,原图中存在的任意一个三元环 ( x , y , c ) (x,y,c) (x,y,c)。不妨假定 i n [ x ] ≤ i n [ y ] ≤ i n [ z ] in[x]\leq in[y]\leq in[z] in[x]in[y]in[z],在新图中一定满足存在 x − > y − > z x->y->z x>y>z x − > z x->z x>z的路径
故直接枚举第一条路径的两个点判断即可
注意这里的三元环是有顺序的,我们需要做到无序,故要将其六种情况全部讨论一遍
再来建图
枚举有边相连的两个点的 g c d gcd gcd,剩余要求互质。
求解过程则是简单问题
注意边用vector存,可以大幅优化常数
但是忽略了两种情况,即选了三个同样的点或者两个同样的点
选三个点时可以直接预处理,选两个点时可以在建边的过程做出来
复杂度估计是不存在的?

旧试题&skyfall

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#include<set>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
using namespace std;
inline int read()
{
	int f=1,x=0;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 stack[20];
inline void write(int x)
{
	if(x<0){putchar('-');x=-x;}
    if(!x){putchar('0');return;}
    int top=0;
    while(x)stack[++top]=x%10,x/=10;
    while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(int x){write(x);putchar('\n');}
const int MAXN=100005;
const int mod=1e9+7;
struct edge
{
	int x,y,c;
	edge(){}
	edge(int _x,int _y,int _c){x=_x;y=_y;c=_c;}
};
vector<pii> vec[MAXN];
vector<edge> vec1;
int mu[MAXN],pr[MAXN],plen;
bool is[MAXN];
void getmu(int N)
{
	mu[1]=1;
	for(int i=2;i<=N;i++)
	{
		if(!is[i])mu[i]=-1,pr[++plen]=i;
		for(int j=1;i*pr[j]<=N&&j<=plen;j++)
		{
			is[i*pr[j]]=true;
			if(!(i%pr[j]))mu[i*pr[j]]=0;
			else mu[i*pr[j]]=-mu[i];
		}
	}
}
int A,B,C,n,m;
LL ans;
LL fa[MAXN],fb[MAXN],fc[MAXN];
void getf(LL *y,int len)
{
	for(int i=1;i<=len;i++)for(int j=1;i*j<=len;j++)y[i]+=len/(i*j);
	for(int i=1;i<=len;i++)y[i]%=mod;
}
void work1(){for(int i=1;i<=m;i++)ans+=fa[i]*fb[i]*fc[i]*mu[i]*mu[i]*mu[i]%mod;}
int du[MAXN];
int gcd(int a,int b){return a==0?b:gcd(b%a,a);}
void prering()
{
	vec1.clear();
	memset(du,0,sizeof(du));
	for(int GCD=1;GCD<=n;GCD++)for(int u1=1;u1*GCD<=n;u1++)if(mu[u1*GCD])
		for(int u2=u1+1;1LL*u2*u1*GCD<=n;u2++)
		if(gcd(u1,u2)==1&&mu[u2*GCD])
		{
			int x=u1*GCD,y=u2*GCD,lcm=u1*u2*GCD;
//			if(lcm>n)continue;
			ans+=mu[x]*mu[x]*mu[y]*(fa[x]*fb[lcm]*fc[lcm]+fa[lcm]*fb[x]*fc[lcm]+fa[lcm]*fb[lcm]*fc[x]);
			ans+=mu[x]*mu[y]*mu[y]*(fa[y]*fb[lcm]*fc[lcm]+fa[lcm]*fb[y]*fc[lcm]+fa[lcm]*fb[lcm]*fc[y]);
			vec1.push_back(edge(x,y,lcm));
			du[x]++;du[y]++;
		}
	for(int i=0;i<vec1.size();i++)
	{
		int x=vec1[i].x,y=vec1[i].y,lcm=vec1[i].c;
		if(du[x]<du[y])vec[x].push_back(mp(y,lcm));
		else vec[y].push_back(mp(x,lcm));
	}
}
int nxt[MAXN],lm[MAXN],tim;
void solve()
{
	for(int i=1;i<=n;i++)if(mu[i])
	{
		tim++;
		for(int j=0;j<vec[i].size();j++)nxt[vec[i][j].first]=tim,lm[vec[i][j].first]=vec[i][j].second;
		for(int j=0;j<vec[i].size();j++)
		{
			int y=vec[i][j].first;
			for(int k=0;k<vec[y].size();k++)
			{
				int z=vec[y][k].first;
				if(nxt[z]!=tim)continue;
				int x=i;
				int lm1=vec[i][j].second,lm2=vec[y][k].second,lm3=lm[z],mul=mu[x]*mu[y]*mu[z];
				ans+=mul*(fa[lm1]*fb[lm2]*fc[lm3]+fa[lm1]*fb[lm3]*fc[lm2]+fa[lm2]*fb[lm1]*fc[lm3]+fa[lm2]*fb[lm3]*fc[lm1]+fa[lm3]*fb[lm1]*fc[lm2]+fa[lm3]*fb[lm2]*fc[lm1]);
			}
		}
	}
}
int main()
{
	getmu(MAXN-1);
	int T=read();while(T--)
	{
		for(int i=1;i<=n;i++)vec[i].clear();
		A=read();B=read();C=read();ans=0;
		memset(fa,0,sizeof(fa));memset(fb,0,sizeof(fb));memset(fc,0,sizeof(fc));
		getf(fa,A);getf(fb,B);getf(fc,C);
		n=max(A,max(B,C));m=min(A,min(B,C));
		memset(nxt,0,sizeof(nxt));tim=0;
		work1();
		prering();
		solve();
		pr2((ans%mod+mod)%mod);
	}
	return 0;
}

约数个数和

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#include<set>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
using namespace std;
inline int read()
{
	int f=1,x=0;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 stack[20];
inline void write(LL x)
{
	if(x<0){putchar('-');x=-x;}
    if(!x){putchar('0');return;}
    int top=0;
    while(x)stack[++top]=x%10,x/=10;
    while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(LL x){write(x);putchar('\n');}
const int MAXN=50005;
int mu[MAXN],pr[MAXN],plen;
bool is[MAXN];
void getpr(int N)
{
	mu[1]=1;
	for(int i=2;i<=N;i++)
	{
		if(!is[i])mu[i]=-1,pr[++plen]=i;
		for(int j=1;i*pr[j]<=N&&j<=plen;j++)
		{
			is[i*pr[j]]=true;
			if(!(i%pr[j]))mu[i*pr[j]]=0;
			else mu[i*pr[j]]=-mu[i];
		}
	}
	for(int i=2;i<=N;i++)mu[i]+=mu[i-1];
}
LL f[MAXN];
int main()
{
	getpr(MAXN-1);
	for(int i=1;i<MAXN;i++)
		for(int j=1,nxt;j<=i;j=nxt+1)
		{
			nxt=i/(i/j);
			f[i]+=1LL*(nxt-j+1)*(i/j);
		}
	int T=read();while(T--)
	{
		int n=read(),m=read();
		if(n>m)swap(n,m);
		LL ans=0;
		for(int i=1,nxt;i<=n;i=nxt+1)
		{
			nxt=min(n/(n/i),m/(m/i));
			ans+=1LL*(mu[nxt]-mu[i-1])*f[n/i]*f[m/i];
		}
		pr2(ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值