题目
Ps:山东的题目可真(du)好(liu),思维+码量的神仙题
推式
求(sum_{i=1}^Asum_{j=1}^Bsum_{k=1}^Cd(ijk))
(Ans=sum_{i=1}^Asum_{j=1}^Bsum_{k=1}^Cd(ijk))
(~~~~~~~=sum_{i=1}^{A}sum_{j=1}^{B}sum_{k=1}^{C}sum_{d|i}sum_{t|j}sum_{p|k}epsilon((d,t))epsilon((t,p))epsilon((d,p)))
(~~~~~~~=sum_{i=1}^{A}sum_{d|i}sum_{j=1}^{B}sum_{t|j}sum_{k=1}^{C}sum_{p|k}epsilon((d,t))epsilon((t,p))epsilon((d,p)))
(~~~~~~~=sum_{d=1}^{A}sum_{i=1}^{lfloorfrac{A}{d} floor}sum_{j=1}^{B}sum_{t|j}sum_{k=1}^{C}sum_{p|k}epsilon((d,t))epsilon((t,p))epsilon((d,p)))
(~~~~~~~=sum_{d=1}^{A}sum_{i=1}^{lfloorfrac{A}{d} floor}sum_{t=1}^{B}sum_{j=1}^{lfloorfrac{B}{t} floor}sum_{p=1}^{C}sum_{k=1}^{lfloorfrac{C} {p} floor}epsilon((d,t))epsilon((t,p))epsilon((d,p)))
(~~~~~~~=sum_{d=1}^{A}sum_{t=1}^{B}sum_{p=1}^{C}lfloorfrac{A}{d} floorlfloorfrac{B}{t} floorlfloorfrac{C}{p} floorepsilon((d,t))epsilon((t,p))epsilon((d,p)))
(~~~~~~~=sum_{d=1}^{A}sum_{t=1}^{B}sum_{p=1}^{C}lfloorfrac{A}{d} floorlfloorfrac{B}{t} floorlfloorfrac{C}{p} floorsum_{u|t&u|d}mu(u)sum_{v|t&v|p}mu(v))
(~~~~~~~~~~~~sum_{w|d&w|p}mu(w))
(~~~~~~~=sum_{u=1}^{min(A,B)}sum_{v=1}^{min(B,C)}sum_{w=1}^{min(A,C)}mu(u)mu(v)mu(w)sum_{u|d&w|d}lfloorfrac{A}{d} floorsum_{u|t&v|t}lfloorfrac{B}{t} floor)
(~~~~~~~~~~~~sum_{v|p&w|p}lfloorfrac{C}{p} floor)
(~~~~~~~=sum_{u=1}^{min(A,B)}sum_{v=1}^{min(B,C)}sum_{w=1}^{min(A,C)}mu(u)mu(v)mu(w)sum_{lcm(u,w)|d}lfloorfrac{A}{d} floorsum_{lcm(u,v)|t}lfloorfrac{B}{t} floor)
(~~~~~~~~~~~~sum_{lcm(v,w)|p}lfloorfrac{C}{p} floor)
(~~~~~~~~~)设有函数(f(n,x)=sum_{n|d}lfloorfrac{x}{d} floor)
(~~~~~~~=sum_{u=1}^{min(A,B)}sum_{v=1}^{min(B,C)}sum_{w=1}^{min(A,C)}mu(u)mu(v)mu(w)f(lcm(u,w),A))
(~~~~~~~~~~~~f(lcm(u,v),B)f(lcm(v,w),C))
剪枝
对于((u,v,w))这个三元组,我们考虑用三元环连边的方法剪枝:
显然:(f(lcm(u,v),x))有效的条件为 (mu(u)*mu(v)!=0)且(lcm(u,v)<x)
那我们怎么连边呢?(lcm(u,v)=ucdot v cdot gcd(u,v)),第一层枚举(lcm(u,v))是肯定的
然后枚举(u)之后你会发现(v)不是唯一的,在范围内枚举再特判时间根本承受不住,那到底怎么做呢?
再枚举一层(gcd(u,v)),从而得到(v)
没错,这是一个无向图,完全没必要嘛,定义成有向的,在建图的时候就特判(u>v),强制有向
这时候是个无序三元组,只会出现一次
再加一个判断:大度点连向小度点,某dalao简单证明后时间复杂度(O(msqrt m))
之前的一次特判:
保证了三元组无重复点,则最后要把此情况补上
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;
typedef long long LL;
const int maxn=1e5+9;
const int mexn=1e6+9;
const LL p=1e9+7;
inline int Read(){
int x(0),f(1); char c=getchar();
while(c<'0'||c>'9'){
if(c=='-') f=-1; c=getchar();
}
while(c>='0'&&c<='9')
x=(x<<3)+(x<<1)+c-'0',c=getchar();
return x*f;
}
int mu[maxn],prime[maxn];
int fj[maxn][20],tp[maxn];
bool visit[maxn];
inline int Gcd(int a,int b){
int c;
while(b){
c=a%b,a=b,b=c;
}
return a;
}
inline void F_phi(int n){
mu[1]=1; int tot(0);
for(int i=2;i<=n;++i){
if(!visit[i]){
prime[++tot]=i,
mu[i]=-1;
}
for(int j=1;j<=tot&&i*prime[j]<=n;++j){
visit[i*prime[j]]=true;
if(i%prime[j]==0)
break;
mu[i*prime[j]]=-mu[i];
}
}
for(int i=1;i<=tot;++i)
for(int j=1;j*prime[i]<=n;++j){
int num(j*prime[i]);
fj[num][tp[num]++]=prime[i];
}
}
struct node{
int to,d;
};
vector<node> edge[maxn];
int Up,A,B,C,cnt;
LL ans;
int fa[maxn],fb[maxn],fc[maxn],eu[mexn],ev[mexn],ed[mexn],du[maxn];
inline void Solve(){
Up=max(A,max(B,C));
if(A>B)
swap(A,B);
if(A>C)
swap(A,C);
if(B>C)
swap(B,C);
for(int i=1;i<=A;++i)
for(int j=1;j*i<=A;++j)
(fa[i]+=A/(i*j))%=p;
for(int i=1;i<=B;++i)
for(int j=1;j*i<=B;++j)
(fb[i]+=B/(i*j))%=p;
for(int i=1;i<=C;++i)
for(int j=1;j*i<=C;++j)
(fc[i]+=C/(i*j))%=p;
for(int i=1;i<=Up;++i){
if(mu[i]==0)
continue;
int tmp=(1<<tp[i])-1;
for(int j=0;j<=tmp;++j){
int u(1);
for(int k=0,ls=j;ls;ls>>=1,++k)
if(ls&1)
u*=fj[i][k];
for(int k=j;;k=(k-1)&j){
int g(1);
for(int t=0,ls=k;ls;ls>>=1,++t)
if(ls&1)
g*=fj[i][t];
int v=(LL)g*i/u;
++du[u],++du[v];
if(u<v){
eu[++cnt]=u,ev[cnt]=v,ed[cnt]=i;
}
if(k==0)
break;
}
}
}
for(int i=1;i<=cnt;++i){
if(du[eu[i]]<du[ev[i]])
swap(eu[i],ev[i]);
edge[eu[i]].push_back((node){ev[i],ed[i]});
}vector<node> ::iterator it1,it2;
int nn(0);
for(int u=1;u<=Up;++u){
for(it1=edge[u].begin();it1!=edge[u].end();++it1){
for(it2=edge[it1->to].begin();it2!=edge[it1->to].end();++it2){
int v3=(LL)it2->to*u/Gcd(it2->to,u);
if(v3>Up)//限制在范围内
continue;
++nn;
int v1=it1->d;int v2=it2->d;
if(mu[u]*mu[it1->to]*mu[it2->to]==1){
(ans+=((LL)fb[v2]*fc[v3]+(LL)fb[v3]*fc[v2]) *fa[v1]+
((LL)fb[v1]*fc[v3]+(LL)fb[v3]*fc[v1]) *fa[v2]+
((LL)fb[v2]*fc[v1]+(LL)fb[v1]*fc[v2]) *fa[v3])%=p;
}else {
(ans-=((LL)fb[v2]*fc[v3]+(LL)fb[v3]*fc[v2]) *fa[v1]+
((LL)fb[v1]*fc[v3]+(LL)fb[v3]*fc[v1]) *fa[v2]+
((LL)fb[v2]*fc[v1]+(LL)fb[v1]*fc[v2]) *fa[v3])%=p;
ans>0?ans:ans+p;
}
}
}/*
六种贡献,如果其中一个有序三元环在前三个sigma就不符合怎么办?
不符合的话在函数f中反正为0
*/
}
for(int i=1;i<=cnt;i++)//一个重复点的三元环
{
if(mu[eu[i]]==1)//two_v
{
(ans+=(LL)fa[ed[i]]*fb[ed[i]]%p*fc[ev[i]]%p+
(LL)fa[ed[i]]*fb[ev[i]]%p*fc[ed[i]]%p+
(LL)fa[ev[i]]*fb[ed[i]]%p*fc[ed[i]]%p)%=p;
}
else{
(ans-=(LL)fa[ed[i]]*fb[ed[i]]%p*fc[ev[i]]%p+
(LL)fa[ed[i]]*fb[ev[i]]%p*fc[ed[i]]%p+
(LL)fa[ev[i]]*fb[ed[i]]%p*fc[ed[i]]%p)%=p;
ans=ans>0?ans:ans+p;
}
if(mu[ev[i]]==1){
(ans+=(LL)fa[ed[i]]*fb[ed[i]]%p*fc[eu[i]]%p+
(LL)fa[ed[i]]*fb[eu[i]]%p*fc[ed[i]]%p+
(LL)fa[eu[i]]*fb[ed[i]]%p*fc[ed[i]]%p)%=p;
}
else{
(ans-=(LL)fa[ed[i]]*fb[ed[i]]%p*fc[eu[i]]%p+
(LL)fa[ed[i]]*fb[eu[i]]%p*fc[ed[i]]%p+
(LL)fa[eu[i]]*fb[ed[i]]%p*fc[ed[i]]%p)%=p;
ans=ans>0?ans:ans+p;
}
}
for(int i=1;i<=min(min(A,B),C);i++)//三个重复点的三元环
{
if(mu[i]==1)
(ans+=(LL)fa[i]*fb[i]%p*fc[i]%p)%=p;
else if(mu[i]==-1){
(ans-=(LL)fa[i]*fb[i]%p*fc[i]%p)%=p;
ans=ans>0?ans:ans+p;
}
}
printf("%lld
",ans);
}
inline void Clear(){
for(int i=1;i<=A;i++)
fa[i]=0;
for(int i=1;i<=B;i++)
fb[i]=0;
for(int i=1;i<=C;i++)
fc[i]=0;
for(int i=1;i<=Up;i++){
vector<node> emp;
swap(emp,edge[i]);
du[i]=0;
}
ans=cnt=0;
}
int main(){
F_phi(100000);
int T=Read();
while(T--){
A=Read(),B=Read(),C=Read();
Solve();
Clear();
}
return 0;
}