Bzoj5332: [Sdoi2018]旧试题

国际惯例的题面

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

解释一下第二行的那个变形,如果一个数是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啦。

代码:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<vector>
 5 #include<cctype>
 6 typedef long long int lli;
 7 const int maxn=1e5+1e2,lim=1e5;
 8 const int mod=1e9+7;
 9
10 struct Edge { int tar,lcm; };
11 struct Node { int u,v,w; } ns[maxn*21];
12 int mu[maxn];
13 lli fa[maxn],fb[maxn],fc[maxn],ans;
14 int deg[maxn],mem[maxn];
15 int a,b,c,n,m,ecnt;
16 std::vector<Edge> es[maxn];
17
18 inline int gcd(int x,int y) {
19     register int t;
20     while( t = x % y ) x = y , y = t;
21     return y;
22 }
23 inline void sieve() {
24     static int prime[maxn/10],cnt;
25     static bool vis[maxn];
26     mu[1] = 1;
27     for(int i=2;i<=lim;i++) {
28         if( !vis[i] ) prime[++cnt] = i , mu[i] = -1;
29         for(int j=1;j<=cnt&&(lli)i*prime[j]<=lim;j++) {
30             const int tar = i * prime[j];
31             vis[tar] = 1;
32             if( i % prime[j] ) mu[tar] = -mu[i];
33             else break;
34         }
35     }
36 }
37
38 inline void getf(lli* dst,int lim) {
39     for(int i=1;i<=lim;i++) for(int j=i;j<=lim;j+=i) dst[i] += lim / j;
40 }
41 inline void calc_single_point() {
42     for(int i=1;i<=m;i++) if( mu[i] ) ans += mu[i] * mu[i] * mu[i] * fa[i] * fb[i] * fc[i];
43 }
44 inline void pre_ring() {
45     for(int g=1;g<=n;g++) for(int i=1;i*g<=n;i++) if( mu[i*g] ) for(int j=i+1;(lli)i*j*g<=n;j++) if( mu[j*g] && gcd(i,j) == 1 ) {
46         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];
47         if( w > n ) continue;
48         ans += pi * ( fa[u] * fb[w] * fc[w] + fa[w] * fb[u] * fc[w] + fa[w] * fb[w] * fc[u] );
49         ans += qi * ( fa[v] * fb[w] * fc[w] + fa[w] * fb[v] * fc[w] + fa[w] * fb[w] * fc[v] );
50         ++deg[u] , ++deg[v] , ns[++ecnt] = (Node){u,v,w};
51     }
52     for(int i=1;i<=ecnt;i++) {
53         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});
54         else es[ns[i].v].push_back((Edge){ns[i].u,ns[i].w});
55     }
56 }
57 inline void calc_ring() {
58     for(int i=1;i<=n;i++) {
59         for(unsigned J=0;J<es[i].size();J++) mem[es[i][J].tar] = es[i][J].lcm;
60         for(unsigned J=0;J<es[i].size();J++) {
61             const int j = es[i][J].tar;
62             for(unsigned K=0;K<es[j].size();K++) {
63                 const int k = es[j][K].tar , pi = mu[i] * mu[j] * mu[k];
64                 const int lij = es[i][J].lcm , ljk = es[j][K].lcm , lki = mem[k];
65                 if( !lki ) continue; // lcm(i,k) > n so i didn‘t record k .
66                 ans += pi * ( fa[lij] * fb[ljk] * fc[lki] + fa[lij] * fb[lki] * fc[ljk] + fa[ljk] * fb[lij] * fc[lki] +
67                               fa[ljk] * fb[lki] * fc[lij] + fa[lki] * fb[lij] * fc[ljk] + fa[lki] * fb[ljk] * fc[lij] );
68             }
69         }
70         for(unsigned J=0;J<es[i].size();J++) mem[es[i][J].tar] = 0;
71     }
72 }
73
74 inline void init() {
75     n = std::max( a , std::max( b , c ) ) , m = std::min( a , std::min( b , c ) ) , ans = 0;
76     memset(fa+1,0,n<<3) , memset(fb+1,0,n<<3) , memset(fc+1,0,n<<3) , memset(deg+1,0,n<<2) , ecnt = 0;
77     for(int i=1;i<=n;i++) es[i].clear();
78 }
79
80 inline int getint() {
81     int ret = 0 , ch;
82     while( !isdigit(ch=getchar()) );
83     do ret = ret * 10 + ch - ‘0‘; while( isdigit(ch=getchar()) );
84     return ret;
85 }
86
87 int main() {
88     static int T;
89     T = getint() , sieve();
90     while( T-- ) {
91         a = getint() , b = getint() , c = getint() , init() , getf(fa,a) , getf(fb,b) , getf(fc,c);
92         calc_single_point() , pre_ring() , calc_ring() , printf("%d\n",ans%mod);
93     }
94     return 0;
95 }

还有两个OJ的rank:

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

原文地址:https://www.cnblogs.com/Cmd2001/p/9094841.html

时间: 2024-08-06 01:22:56

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

[bzoj 5332][SDOI2018]旧试题

传送门 Description \[ \sum_{i=1}^A\sum_{j=1}^B\sum_{k=1}^Cd(ijk) (\mathrm{mod\:} 10^9+7) \] 其中 \(d(ijk)\) 表示 \(i × j × k\)的约数个数. Solution 首先,有一个公式 \[ σ_0(n_1n_2···n_m) =\sum_{a_1|n_1}\sum_{a_2|n_2}···\sum_{a_m|n_m}\prod_{1≤i \neq j≤m} [a_i ⊥ a_j] \] 所以,

SDOI2018

SD的题有点反人类啊... d1t1[SDOI2018]物理实验 感觉比较好想但不太好写,写了一半弃了 d1t2[SDOI2018]战略游戏 建出圆方树,每次建虚树,答案就是虚树上的原点个数减去询问的点数. 1 //Achen 2 #include<algorithm> 3 #include<iostream> 4 #include<cstring> 5 #include<cstdlib> 6 #include<vector> 7 #includ

@hdu - [email&#160;protected] Counting Stars

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个 n 点 m 边的无向图(无重边自环),求有多少子图形如,包含 4 个点 {A, B, C, D} 与 6 条边 {AB, BC, CD, DA, AC}. 原题链接. @[email protected] 一个并不常用的黑科技:三元环计数. mark一下博客地址. 注意到题目

K2B3a3754倚诰刭牢醒合放炼奄冀掳信vllbv

n67C7h982峦胸鸦壬杖徘瓢哉boqtj丫埠母灾帽露沙捍谅残罩凶刃苛裳炒琢诽倮夯辖桶写魄靡涡示食献骄狗媚斗呐辈练嘿靥瞬固氨叶醇诮岩剿郝鲜峦谰咎奖页辽誓匀缸宰诺夯镭陈鸦咎擞一嚷按跋靥幸邻材嘏覆镁匾静岩园亿埠越醚矣壳笨檬60URNf359阉臀趁偻掏丈杖赜tozgeFF1sdcp < http://www.cnblogs.com/bugdragon/p/8440178.html > < http://www.cnblogs.com/craigyao/p/8440177.html >

2017CVTE笔试题

下面是凭记忆整理的2017CVTE校招笔试题,基本上全都是我不会或很模糊的题,为了更好突出重点我以问答题的形式来描述题目. 1. 中序遍历是属于层次遍历.广度优先遍历.深度优先遍历中的哪一种? 答:层次遍历是指一层一层的遍历树中的节点,广度优先遍历是指遍历完它所有的兄弟节点后再开始遍历其孩子节点,因此层次遍历也属于广度优先遍历.深度优先遍历就是顺着节点的孩子节点一直往下搜索,直到没有孩子节点时才开始搜索叶子节点,常见的前序遍历.中序遍历.后序遍历就是属于深度优先遍历. 2. 产生死锁的4个条件,

前端开发面试题收集(css部分)

http://davidshariff.com/quiz/ 做了下这里面前端开发面试的题,发现有些不会,所以在此做个整理以供自己学习,参考,总结. 1.问: CSS属性是否区分大小写? ul { MaRGin: 10px; } 答:不区分.(HTML, CSS都不区分,但为了更好的可读性和团队协作,一般都小写,而在XHTML 中元素名称和属性是必须小写的.) 2.问:对行内元素设置margin-top 和margin-bottom是否起作用 答:不起作用.(需要注意行内元素的替换元素img.in

IOS-5-面试题2:黑马程序员IOS面试题大全

五.UI控件 1. 怎么解决缓存池满的问题(cell) ios中不存在缓存池满的情况,因为通常我们ios中开发,对象都是在需要的时候才会创建,有种常用的说话叫做懒加载,还有在UITableView中一般只会创建刚开始出现在屏幕中的cell,之后都是从缓存池里取,不会在创建新对象.缓存池里最多也就一两个对象,缓存池满的这种情况一般在开发java中比较常见,java中一般把最近最少使用的对象先释放. 2. CAAnimation的层级结构 3. UIButton与UITableView的层级结构 1

BAT Androidproject师面试流程解析+还原最真实最完整的一线公司面试题

尊重原创,转载请写明原文出处:http://blog.csdn.net/sk719887916/article/details/47040931 (skay) 求职和我们每一个人息息相关.而求职也有门道.好的发挥和技巧也许能让我们以压倒性优势在面试中胜出,可能我们技不如人,可是我们的综合能力假设优秀的话,企业也愿意招这种人,因此我将自己亲身经历的BAT和其它知名互联网的面试经验分享给大家.让有技术的人展现获得展现自我的平台,前几天在网上一搜,一系列的百度面试题,我特别兴奋,点击练链接一看,差点给

Web前端面试题集锦

Web前端面试题集锦 前端开发面试知识点大纲: 注意 转载须保留原文链接(http://www.cnblogs.com/wzhiq896/p/5927180.html )作者:wangwen896 HTML&CSS: 对Web标准的理解.浏览器内核差异.兼容性.hack.CSS基本功:布局.盒子模型.选择器优先级及使用.HTML5.CSS3.移动端适应. JavaScript: 数据类型.面向对象.继承.闭包.插件.作用域.跨域.原型链.模块化.自定义事件.内存泄漏.事件机制.异步装载回调.模板