LOJ2565. 「SDOI2018」旧试题

给出\(A,B,C\),求\(\sum_{i=1}^{A}\sum_{j=1}^{B}\sum_{k=1}^C \sigma_0(ijk)\)

\(\sigma_0(n)\)表示\(n\)的约数个数。

\(A,B,C\le 10^5\)


结论:\(\sigma_0(ijk)=\sum_{a|i}\sum_{b|j}\sum_{c|k}[b\perp c][a \perp c][a \perp b]\)

证明类比只有两个数的情况。

接下来推式子,三项东西同步推,得到:\(\sum_i\sum_j\sum_k\mu(i)\mu(j)\mu(k)f(A,[j,k])f(B,[i,k])f(C,[i,j])\)

其中\(f(n,d)=\sum_{d|i}\frac{n}{i}\)。暴力求时间\(O(n\ln n)\)

神仙转化:建图,找三元环\((i,j,k)\)

建图的时候要求\(lcm\)不超过\(n\)。于是连出来的边实际上只有不到\(8*10^5\)条。

找三元环:求出每个点的度数,把边定向为从大度数到小度数。枚举某个点\(u\),先枚举\((u,w)\in E\)\(w\)打上标记,再枚举\((u,v),(v,w)\in E\),若\(w\)有标记则找到一个三元环。

时间复杂度:其实相当于对于\((u,v)\in E\),给\(out_v\)求和。如果\(deg_v\le \sqrt m\),则\(out_v\le \sqrt m\),总共\(O(m\sqrt m)\);如果\(deg_v\ge \sqrt m\),这样的\(v\)\(u\)不超过\(\sqrt m\)个,于是\(out_v\)最多被贡献\(\sqrt m\)次,总共也是\(O(m\sqrt m)\)

还有更加神仙的不用找三元环的方法当然我不会……


using namespace std;
#include <bits/stdc++.h>
#define N 100005
#define ll long long
#define mo 1000000007
int gcd(int a,int b){
	int k;
	while (b)
		k=a%b,a=b,b=k;
	return a;
}
int lcm(int a,int b){
//	assert((ll)a/gcd(a,b)*b<=100005);
	return a/gcd(a,b)*b;
}
int A[3],n;
int p[N],np;
bool inp[N];
int mu[N],mnp[N];
void initp(int n){
	mu[1]=1;
	for (int i=2;i<=n;++i){
		if (!inp[i])
			p[++np]=i,mu[i]=-1,mnp[i]=i;
		for (int j=1;j<=np && i*p[j]<=n;++j){
			inp[i*p[j]]=1;
			mnp[i*p[j]]=p[j];
			if (i%p[j]==0)
				break;
			mu[i*p[j]]=-mu[i];
		}
	}
}
int f[3][N];
void initf(int g[],int n){
	for (int i=1;i<=n;++i){
		ll sum=0;
		for (int j=1;i*j<=n;++j)
			sum+=n/(i*j);
		g[i]=sum%mo;
	}
}
int deg[N];
struct EDGE{
	int to,w;
	EDGE *las;
} e[1000000];
int ne;
EDGE *last[N];
void link(int u,int v,int w){
	e[ne]={v,w,last[u]};
	last[u]=e+ne++;
}
struct edge{int u,v,w;} ed[1000000];
int q[N],re[N],totv,tote;
bool cmpq(int x,int y){return deg[x]>deg[y];}
ll ans;
void calc(int a){
	ll s=(ll)f[0][a]*f[1][a]%mo*f[2][a]%mo;
	ans+=mu[a]*s;
}
void calc(int a,int b,int ab){
	ll s=0;
	s+=(ll)f[0][b]*f[1][ab]%mo*f[2][ab];
	s+=(ll)f[0][ab]*f[1][b]%mo*f[2][ab];
	s+=(ll)f[0][ab]*f[1][ab]%mo*f[2][b];
	s%=mo;
	ans+=mu[a]*s;
}
void calc(int a,int b,int c,int ab,int ac,int bc){
	ll s=0;
	s+=(ll)f[0][ab]*f[1][ac]%mo*f[2][bc];
	s+=(ll)f[0][ab]*f[2][ac]%mo*f[1][bc];
	s+=(ll)f[1][ab]*f[0][ac]%mo*f[2][bc];
	s+=(ll)f[1][ab]*f[2][ac]%mo*f[0][bc];
	s+=(ll)f[2][ab]*f[0][ac]%mo*f[1][bc];
	s+=(ll)f[2][ab]*f[1][ac]%mo*f[0][bc];
	s%=mo;
	ans+=mu[a]*mu[b]*mu[c]*s;
}
int W,d[N],z[N],dz[N],cp;
void divide(int x){
	cp=0;
	while (x!=1){
		d[++cp]=mnp[x];
		z[cp]=0,dz[cp]=1;
		while (x%d[cp]==0)
			z[cp]++,x/=d[cp],dz[cp]*=d[cp];
	}
}
void find(int k,int u,int v){
	if (k>cp){
		if (u<v && mu[u] && mu[v]){
			ed[++tote]={u,v,W};
			deg[u]++,deg[v]++;
		}
		return;
	}
	int pro=1;
	for (int i=0;i<z[k];++i,pro*=d[k]){
		find(k+1,u*pro,v*dz[k]);
		find(k+1,u*dz[k],v*pro);
	}
	find(k+1,u*dz[k],v*dz[k]);
}
void work(){
	memset(deg,0,sizeof(int)*(n+1));
	totv=0,tote=0;
	for (int i=1;i<=n;++i){
		W=i;
		divide(i);
		find(1,1,1);
	}
	for (int i=1;i<=n;++i)
		if (mu[i])
			q[++totv]=i;
	sort(q+1,q+totv+1,cmpq);
	for (int i=1;i<=totv;++i)
		re[q[i]]=i;
	ne=0;
	memset(last,0,sizeof(EDGE*)*(n+1));
	for (int i=1;i<=tote;++i){
		int u=ed[i].u,v=ed[i].v;
		if (re[u]>re[v])
			swap(u,v);
		link(u,v,ed[i].w);
	}
	static int bz[N];
	for (int i=1;i<=totv;++i){
		int u=q[i];
		for (EDGE *ei=last[u];ei;ei=ei->las)
			bz[ei->to]=ei->w;
		for (EDGE *ei=last[u];ei;ei=ei->las){
			calc(u,ei->to,ei->w);
			calc(ei->to,u,ei->w);
			for (EDGE *ej=last[ei->to];ej;ej=ej->las)
				if (bz[ej->to])
					calc(u,ei->to,ej->to,ei->w,ej->w,bz[ej->to]);
		}
		for (EDGE *ei=last[u];ei;ei=ei->las)
			bz[ei->to]=0;
		calc(u);
	}
}
int main(){
//	freopen("in.txt","r",stdin);
	initp(100000);
	int T;
	scanf("%d",&T);
	while (T--){
		scanf("%d%d%d",&A[0],&A[1],&A[2]);
		sort(A,A+3);
		n=A[2];
		for (int k=0;k<3;++k){
			memset(f[k],0,sizeof(int)*(n+1));
			initf(f[k],A[k]);
		}
		ans=0;
		work();	
		ans=(ans%mo+mo)%mo;
		printf("%lld\n",ans);
	}
	return 0;
}
posted @ 2021-01-18 20:25  jz_597  阅读(99)  评论(0编辑  收藏  举报