国际惯例的题面

首先我们进行一些相对显然的数学变化。

解释一下第二行的那个变形,如果一个数是ijk的因数,那么它一定能被分解成三部分分别是i,j,k的因数。
我们钦定一个质数只能在三部分的一个中出现。如果一个质数仅在ijk中的一个数中出现这样显然是对的,而在多个中出现的话,它贡献答案的次数为它出现的总次数+1次。
而考虑把ijk的乘积分解质因数,然后考虑每个质数的贡献,会发现每个质数贡献答案的次数恰好为它的次数+1次,所以这样是对的。
然后就是分析最后的这个公式了。
右边的三个小求和号里的东西显然可以大力nlog筛预处理一波,这样我们在知道lcm的时候能够O(1)求出这个求和号中的内容。
然后就是前面的东西了。
我们考虑枚举两个数,如果它们的lcm<=max(a,b,c)则相互连边,那么,对答案有贡献的显然就是图中的三元环了。
枚举三元环的话,我们显然有msqrt(m)的算法,其中m为边数。
这种做法就是把双向边都建成单向边,按照度数小的向大的点连或者反之(就像treap大根堆小根堆一样,只要一致了就行),然后沿着边暴力枚举三个点即可,复杂度就是是msqrt(m)了(别问我证明,我不会QAQ)。
考虑lcm比max(a,b,c)小的数对很少,所以我们这题可以......卡过去。
既然是卡过去显然就要卡常数了......
首先μ为0的数我们显然不用算是吧。
另外直接枚举三元环的细节太多(这题存在自环),于是我们先单独计算三个数相同的情况和三个数中有两个相同的情况。
存储边用vector存,在大量寻址的时候vector对缓存更加友好。
计算过程不用取模,最后输出再取模,因为答案不会超过long long(虽然听起来很没有道理),中间即使溢出了也没有关系,只要别溢出超过一轮就行了。
另外最重要的,当你枚举按照边了三个点u,v,w后,是否要计算w和u的lcm来判断两者间是否有边呢?
看起来是需要的,然而并不用!
因为我们连边是存在单调性的,所以我们最终枚举出的三元环一定是u->v->w,u->w的形式。
于是我们在枚举三个点的时候可以先遍历一下u的出边标记下所有可行的w,然后枚举u->v->w之后直接查询lcm就好啦!
于是这样写就跑的飞起了,在BZOJ和洛谷上都是rank1啦。

代码:

 #include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cctype>
typedef long long int lli;
const int maxn=1e5+1e2,lim=1e5;
const int mod=1e9+; struct Edge { int tar,lcm; };
struct Node { int u,v,w; } ns[maxn*];
int mu[maxn];
lli fa[maxn],fb[maxn],fc[maxn],ans;
int deg[maxn],mem[maxn];
int a,b,c,n,m,ecnt;
std::vector<Edge> es[maxn]; inline int gcd(int x,int y) {
register int t;
while( t = x % y ) x = y , y = t;
return y;
}
inline void sieve() {
static int prime[maxn/],cnt;
static bool vis[maxn];
mu[] = ;
for(int i=;i<=lim;i++) {
if( !vis[i] ) prime[++cnt] = i , mu[i] = -;
for(int j=;j<=cnt&&(lli)i*prime[j]<=lim;j++) {
const int tar = i * prime[j];
vis[tar] = ;
if( i % prime[j] ) mu[tar] = -mu[i];
else break;
}
}
} inline void getf(lli* dst,int lim) {
for(int i=;i<=lim;i++) for(int j=i;j<=lim;j+=i) dst[i] += lim / j;
}
inline void calc_single_point() {
for(int i=;i<=m;i++) if( mu[i] ) ans += mu[i] * mu[i] * mu[i] * fa[i] * fb[i] * fc[i];
}
inline void pre_ring() {
for(int g=;g<=n;g++) for(int i=;i*g<=n;i++) if( mu[i*g] ) for(int j=i+;(lli)i*j*g<=n;j++) if( mu[j*g] && gcd(i,j) == ) {
const int u = i * g , v = j * g , w = i * j * g , pi = mu[u] * mu[u] * mu[v] , qi = mu[u] * mu[v] * mu[v];
if( w > n ) continue;
ans += pi * ( fa[u] * fb[w] * fc[w] + fa[w] * fb[u] * fc[w] + fa[w] * fb[w] * fc[u] );
ans += qi * ( fa[v] * fb[w] * fc[w] + fa[w] * fb[v] * fc[w] + fa[w] * fb[w] * fc[v] );
++deg[u] , ++deg[v] , ns[++ecnt] = (Node){u,v,w};
}
for(int i=;i<=ecnt;i++) {
if( deg[ns[i].u] < deg[ns[i].v] || ( deg[ns[i].u] == deg[ns[i].v] && ns[i].u < ns[i].v ) ) es[ns[i].u].push_back((Edge){ns[i].v,ns[i].w});
else es[ns[i].v].push_back((Edge){ns[i].u,ns[i].w});
}
}
inline void calc_ring() {
for(int i=;i<=n;i++) {
for(unsigned J=;J<es[i].size();J++) mem[es[i][J].tar] = es[i][J].lcm;
for(unsigned J=;J<es[i].size();J++) {
const int j = es[i][J].tar;
for(unsigned K=;K<es[j].size();K++) {
const int k = es[j][K].tar , pi = mu[i] * mu[j] * mu[k];
const int lij = es[i][J].lcm , ljk = es[j][K].lcm , lki = mem[k];
if( !lki ) continue; // lcm(i,k) > n so i didn't record k .
ans += pi * ( fa[lij] * fb[ljk] * fc[lki] + fa[lij] * fb[lki] * fc[ljk] + fa[ljk] * fb[lij] * fc[lki] +
fa[ljk] * fb[lki] * fc[lij] + fa[lki] * fb[lij] * fc[ljk] + fa[lki] * fb[ljk] * fc[lij] );
}
}
for(unsigned J=;J<es[i].size();J++) mem[es[i][J].tar] = ;
}
} inline void init() {
n = std::max( a , std::max( b , c ) ) , m = std::min( a , std::min( b , c ) ) , ans = ;
memset(fa+,,n<<) , memset(fb+,,n<<) , memset(fc+,,n<<) , memset(deg+,,n<<) , ecnt = ;
for(int i=;i<=n;i++) es[i].clear();
} inline int getint() {
int ret = , ch;
while( !isdigit(ch=getchar()) );
do ret = ret * + ch - ''; while( isdigit(ch=getchar()) );
return ret;
} int main() {
static int T;
T = getint() , sieve();
while( T-- ) {
a = getint() , b = getint() , c = getint() , init() , getf(fa,a) , getf(fb,b) , getf(fc,c);
calc_single_point() , pre_ring() , calc_ring() , printf("%d\n",ans%mod);
}
return ;
}

还有两个OJ的rank:

生きてくって多分
只要活着
越えてかなきゃいけないよね
大概就不得不去跨越吧
その勇気で夢の欠片が
鼓起勇气
少しずつ動き出す
一点一点地转动梦的碎片
終わりが始まる
即将迎来终结
何処へ行くのなんて
连目标为何都不知道
わからないまま
就这样迷茫地
ただ過ぎてゆく
度过

Bzoj5332: [Sdoi2018]旧试题的更多相关文章

  1. BZOJ5332: [Sdoi2018]旧试题(莫比乌斯反演)

    时光匆匆,转眼间又是一年寒暑…… 这是小 Q 同学第二次参加省队选拔赛. 今年,小 Q 痛定思痛,不再冒险偷取试题,而是通过练习旧 试题提升个人实力.可是旧试题太多了,小 Q 没日没夜地做题,却看不到 ...

  2. 【BZOJ5332】[SDOI2018]旧试题(数论,三元环计数)

    [BZOJ5332][SDOI2018]旧试题(数论,三元环计数) 题面 BZOJ 洛谷 题解 如果只有一个\(\sum\),那么我们可以枚举每个答案的出现次数. 首先约数个数这个东西很不爽,就搞一搞 ...

  3. [SDOI2018] 旧试题

    推狮子的部分 \[ \sum_{i=1}^A\sum_{j=1}^B\sum_{k=1}^C\sigma(ijk) =\sum_{i=1}^A\sum_{j=1}^B\sum_{k=1}^C\sum_ ...

  4. P4619 [SDOI2018]旧试题

    题目 P4619 [SDOI2018]旧试题 Ps:山东的题目可真(du)好(liu),思维+码量的神仙题 推式 求\(\sum_{i=1}^A\sum_{j=1}^B\sum_{k=1}^Cd(ij ...

  5. sdoi2018旧试题 证明

  6. LOJ2565 SDOI2018 旧试题 莫比乌斯反演、三元环计数

    传送门 这道题的思路似乎可以给很多同时枚举三个量的反演题目提供一个很好的启发-- 首先有结论:\(d(ijk) = \sum\limits_{x|i}\sum\limits_{y|j}\sum\lim ...

  7. [bzoj 5332][SDOI2018]旧试题

    传送门 Description \[ \sum_{i=1}^A\sum_{j=1}^B\sum_{k=1}^Cd(ijk) (\mathrm{mod\:} 10^9+7) \] 其中 \(d(ijk) ...

  8. loj#2565. 「SDOI2018」旧试题(反演 三元环计数)

    题意 题目链接 Sol 神仙反演题.在洛谷上疯狂被卡常 Orz shadowice #include<bits/stdc++.h> #define Pair pair<int, in ...

  9. LOJ2476. 「2018 集训队互测 Day 3」蒜头的奖杯 & LOJ2565. 「SDOI2018」旧试题(莫比乌斯反演)

    题目链接 LOJ2476:https://loj.ac/problem/2476 LOJ2565:https://loj.ac/problem/2565 题解 参考照搬了 wxh 的博客. 为了方便, ...

随机推荐

  1. ROS学习笔记(二) # ROS NodeHandles

    1. 自动启动和关闭 ros::NodeHandle nh: 这段代码执行之后,如果内部节点还没有启动,ros::NodeHandle 会启动这个节点:一旦所有的 ros::NodeHandle 实例 ...

  2. WPF开发中的多线程的问题

    今天帮助同事做了一个WPF版的多线程demo,分享给大家. 要实现的问题就是非主线程thread1 去后台不停的取新数据,当有新数据的时候就会展示到前台. 我给他做的demo实现一个按秒的计数器,随着 ...

  3. jQuery的end() 方法

    定义和用法 end() 方法结束当前链条中的最近的筛选操作,并将匹配元素集还原为之前的状态. 语法 .end() 详细说明 大多数 jQuery 的遍历方法会操作一个 jQuery 对象实例,并生成一 ...

  4. linux 下程序员专用搜索源码用来替代grep的软件ack(后来发现一个更快的: ag), 且有vim插件的

    发现一个比ack更快更好用的:  https://github.com/ggreer/the_silver_searcher   , 使用时命令为ag,它是基于ack的代码二次开发的,所有使用方法基本 ...

  5. java多线程系列五、并发容器

    一.ConcurrentHashMap 1.为什么要使用ConcurrentHashMap 在多线程环境下,使用HashMap进行put操作会引起死循环,导致CPU利用率接近100%,HashMap在 ...

  6. Hive官方使用手册——新Hive CLI(Beeline CLI)

    Hive官方使用手册——新Hive CLI(Beeline CLI) https://blog.csdn.net/maizi1045/article/details/79481686

  7. TCP/IP、Http大纲

    TPC/IP协议是传输层协议,主要解决数据如何在网络中传输,而HTTP是应用层协议,主要解决如何包装数据.关于TCP/IP和HTTP协议的关系,网络有一段比较容易理解的介绍:“我们在传输数据时,可以只 ...

  8. Math 对象

    Math对象提供了,我们一般进行数学运算的所有函数. Math.random() 随机0~1之间的随机数 [0, 1) Math.max() 求传入参数的最大数 Math.min() 求传入参数的最小 ...

  9. 微信公众号开发JS-SDK(1.2)

    概述 微信js-SDK是微信公众平台面向网页开发者提供的基于微信内的网页开发工具包. 通过使用微信JS-SDK,网页开发者可借助微信高效地使用拍照.选图.语音.位置等手机系统的能力,同时可以直接使用微 ...

  10. Visual Studio强大的帮助工具--Resharper安装与使用

    ReSharper是一个JetBrains公司出品的代码生成工具,其能帮助Microsoft Visual Studio成为一个更佳的IDE.使用ReSharper,你可以进行深度代码分析,智能代码协 ...