HDU 5381 The sum of gcd (技巧,莫队算法)
题意:有一个含n个元素的序列,接下来有q个询问区间,对每个询问区间输出其 f(L,R) 值。

思路:
天真单纯地以为是道超级水题,不管多少个询问,计算量顶多就是O(n2) ,就是暴力穷举每个区间,再直接开个1e8大的int数组保存其结果不就行了?呵呵,限制你内存,看你怎么死!即使给了你这么大的内存,O(n2) 也不容易过,计算量偏大,少一点也许可以。
贴个O(n2)代码。
#include <bits/stdc++.h>
#define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
#define pii pair<int,int>
#define INF 0x7f7f7f7f
#define LL long long
using namespace std;
const int N=;
struct node
{
int L, R, ans;
}que[N];
struct node1
{
int ll, rr, gcd;
node1(){};
node1(int ll,int rr,int gcd):ll(ll),rr(rr),gcd(gcd){};
}reg[N][]; int _gcd(int a, int b){return b == ? a : _gcd(b, a % b);} int seq[N], n, q, pre[N];
int cnt[N];
void pre_cal()
{
memset(cnt,,sizeof(cnt));
reg[n][]=node1(n,n,seq[n]); //最后一个特殊处理
cnt[n]++;
for(int i=n-; i>=; i--)
{
int R=i, gcd=seq[i], tmp;
for(int j=; j<cnt[i+]; j++)
{
node1 &t=reg[i+][j];
tmp=_gcd(gcd, t.gcd );
if(tmp!=gcd) reg[i][cnt[i]++]=node1(i, R, gcd);
gcd=tmp;
R=t.rr;
}
reg[i][cnt[i]++]=node1(i, R, gcd);
}
} void cal()
{
for(int i=; i<=n; i++)
{
pre[i-]=;
pre[i]=seq[i];
int p=;
for(int j=i+; j<=n; j++)
{
if(j>reg[i][p].rr) p++;
pre[j]=reg[i][p].gcd;
pre[j]+=pre[j-];
}
for(int j=; j<=q; j++)
if( i>=que[j].L && i<=que[j].R )
que[j].ans+=pre[que[j].R]-pre[i-];
}
for(int i=; i<=q; i++) printf("%d\n",que[i].ans);
}
int main()
{
//freopen("input.txt", "r", stdin);
int t, L, R;
cin>>t;
while(t--)
{
scanf("%d", &n);
for(int i=; i<=n; i++) scanf("%d", &seq[i]); scanf("%d", &q);
for(int i=; i<=q; i++)
{
scanf("%d %d", &que[i].L, &que[i].R);
que[i].ans=;
}
pre_cal();
cal();
} return ;
}
TLE代码
接着是尝试其他方法,分块!主要是复杂度为啥是O(n1.5*32),分析:
(1)假如有10000个元素,那么分成100块,每块100个元素。将所有询问保存,按照 (块号,R) 来排序(暂不管他L是否是有序)。
(2)如果我们可以在O(32*n)的时间内计算出任意一个块的所有查询,那么仅有100块,每块O(32*n),那么就是O(32*n1.5)。为虾米可以在O(32*n)内.....?
(3)拿第一个块来分析,因为它可能是计算量最大的,比如来个询问[1,1],接着来个[100,10000],再来个[2,100]。拿出两个最极限的例子讲,[1,1]和[100,10000],这也是O(32*n)的。看成是两个点,这两个点的曼哈顿距离是10100,这就是O(n)了。在[1,1]往[100,10000]转移过程中,每移动一单位的曼哈顿距离时至少是需要O(logN)的,就是计算GCD的复杂度而已!
(4)每单位的曼哈顿距离如何转移才能O(logN)?这需要预处理了。假设要从[1,5]转移到[1,6],只要答案加上以Right[6][1]即可,Right[6][1]定义为 gcd[i....6]之和,即Right[6][1]=gcd[6,6]+gcd[5,6]+gcd[4,5,6]+gcd[3,4,5,6]+gcd[2,3,4,5,6]+gcd[1,2,3,4,5,6]。
所以要先预处理出所有的Right和Left(相反而已),复杂度O(nlogn),注意,并不是真的是这样记录的Right[6][1],实际上记录的是,以6为R,如果gcd相同,记录其L位置,由于随着数字越来越多,gcd肯定是会变小或者不变,而每次变小起码少一半,那结果最多纪录32个就行了。即使这样,我们还得靠递推的关系来使复杂度达到O(nlogn),否则仍不行。
递推是这样的,假设知道以i为结尾的所有段都知道了,则可以靠Right[i]的32个段推出32个Right[i+1]的段(32指的是上限,不是实际)。这只是新加入了个数字在原来的基础上多了个数字seq[i+1],若seq[i+1]跟Right[i][1]的gcd(记作x)不是seq[i+1],那么seq[i+1]独自可作为一段,记为Right[i+1][0], 接着按Right[i][1]的L继续往左扩展。(好啰嗦!不如画矩阵看来得直接,总之,其实满足递推关系的,而且是线性的。)
可以看看这篇!http://blog.csdn.net/u014800748/article/details/47680899
注意:没有用longlong会挂!
#include <bits/stdc++.h>
#define pii pair<int,int>
#define INF 0x7f7f7f7f
#define LL long long
using namespace std;
const int N=;
struct node
{
int L, R, pos;
LL ans;
}que[N]; struct node1
{
int ll, rr;
LL gcd;
node1(){};
node1(int ll,int rr,LL gcd):ll(ll),rr(rr),gcd(gcd){};
};
int seq[N], n, q, blocks;
vector<node1> Left[N], Right[N];
inline int cmp(node a,node b)
{
if(a.L/blocks == b.L/blocks ) return a.R<b.R;
return a.L < b.L; //既然不同块,大的肯定在后面
}
inline int cmp1(node a,node b){return a.pos<b.pos;} void pre_cal() //计算右上三角,左下三角
{
LL gcd;
int L, R, tmp;
//************固定左边Left*********************
Left[n].push_back(node1(n, n, seq[n])); //最后一个特殊处理
for(int i=n-; i>=; i--)
{
L=R=i;
gcd=seq[i];
for(int j=; j<Left[i+].size(); j++)
{
node1 &t=Left[i+][j];
tmp=__gcd(gcd, t.gcd );
if(tmp!=gcd)
{
Left[i].push_back(node1(L, R, gcd));
L=R+;
gcd=tmp;
}
R=t.rr;
}
Left[i].push_back(node1(L, n, gcd));
} //************固定右边Right*******************
Right[].push_back(node1(, , seq[])); //最后一个特殊处理
for(int i=; i<=n; i++)
{
L=R=i;
gcd=seq[i];
for(int j=; j<Right[i-].size(); j++)
{
node1 &t=Right[i-][j];
tmp=__gcd(gcd, t.gcd);
if(tmp!=gcd)
{
Right[i].push_back(node1(L, R, gcd));
R=L-;
gcd=tmp;
}
L=t.ll;
}
Right[i].push_back(node1(, R, gcd));
}
} LL updateR(int L,int R)
{
LL ans=;
for(int i=; i<Right[R].size(); i++)
{
if( L >= Right[R][i].ll && L<=Right[R][i].rr )
{
ans+= ( Right[R][i].rr - L + ) * Right[R][i].gcd;
return ans;
}
ans+=( Right[R][i].rr - Right[R][i].ll + ) * Right[R][i].gcd;
}
}
LL updateL(int L,int R)
{
LL ans=;
for(int i=; i<Left[L].size(); i++)
{
if(R <= Left[L][i].rr && R>=Left[L][i].ll )
{
ans+=( R - Left[L][i].ll + ) * Left[L][i].gcd;
return ans;
}
ans+=( Left[L][i].rr - Left[L][i].ll + ) * Left[L][i].gcd;
}
} void cal()
{
sort(que, que+q, cmp);
LL ans=;
for(int i=, L=, R=; i<q; i++)
{
//**************将R逐步拉到相应位置******************
for( ; R>que[i].R; R--) ans-=updateR(L, R);
for( ; R<que[i].R; R++) ans+=updateR(L, R+); //**************将L逐步拉到相应位置******************
for( ; L<que[i].L; L++) ans-=updateL( L, R);
for( ; L>que[i].L; L--) ans+=updateL( L-, R); que[i].ans=ans;
}
sort(que, que+q, cmp1);
for(int i=; i<q; i++) printf("%lld\n", que[i].ans);
} int main()
{
freopen("input.txt", "r", stdin);
int t;
cin>>t;
for(int i=; i<N; i++) que[i].pos=i;
while(t--)
{
for(int i=; i<N; i++) Left[i].clear(),Right[i].clear(); scanf("%d", &n);
for(int i=; i<=n ; i++) scanf("%d", &seq[i]);
scanf("%d", &q);
for(int i=; i<q; i++) scanf("%d %d", &que[i].L, &que[i].R);
blocks=sqrt(n);
pre_cal(); //计算辅助数组
cal();
} return ;
}
AC代码
还有一种线段树的方法,好像差不多思想:
看这篇:http://www.cnblogs.com/CSU3901130321/p/4733701.html
附上莫队算法学习blog:http://blog.csdn.net/mlzmlz95/article/details/43644653
HDU 5381 The sum of gcd (技巧,莫队算法)的更多相关文章
- HDU - 4676 :Sum Of Gcd (莫队&区间gcd公式)
Given you a sequence of number a 1, a 2, ..., a n, which is a permutation of 1...n. You need to answ ...
- hdu 5381 The sum of gcd(线段树+gcd)
题目链接:hdu 5381 The sum of gcd 将查询离线处理,依照r排序,然后从左向右处理每一个A[i],碰到查询时处理.用线段树维护.每一个节点表示从[l,i]中以l为起始的区间gcd总 ...
- hdu 5381 The sum of gcd
知道对于一个数列,如果以x为左(右)端点,往右走,则最多会有log(a[x])个不同的gcd,并且有递减性 所以会分成log段,每一段的gcd相同 那我们可以预处理出对于每一个位置,以这个位置为左端点 ...
- hdu 5381 The sum of gcd 莫队+预处理
The sum of gcd Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) P ...
- hdu 5381 The sum of gcd 2015多校联合训练赛#8莫队算法
The sum of gcd Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) T ...
- HDU 4676 Sum Of Gcd 【莫队 + 欧拉】
任意门:http://acm.hdu.edu.cn/showproblem.php?pid=4676 Sum Of Gcd Time Limit: 10000/5000 MS (Java/Others ...
- 2015 Multi-University Training Contest 8 hdu 5381 The sum of gcd
The sum of gcd Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)To ...
- 【HDU4676】Sum Of Gcd(莫队+欧拉函数)
点此看题面 大致题意: 多组询问,求\(\sum_{i=L}^R\sum_{j=i+1}^Rgcd(i,j)\). 推式子 这道题我们可以考虑,每个因数\(d\)被统计答案的次数,肯定与其出现次数有关 ...
- hdu 4358 Boring counting 离散化+dfs序+莫队算法
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4358 题意:以1为根节点含有N(N <= 1e5)个结点的树,每个节点有一个权值(weight ...
随机推荐
- IOS AutoLayout 代码实现约束—VFL
在autolayout下,尽管使用IB来拖放控件,但仍然避免不了用代码来创建控件,这是约束需要代码来实现. IOS 提供了两种添加约束的方法 第一种: +(instancetype)constrain ...
- 【HDU 4819】Mosaic
[题目链接] 点击打开链接 [算法] 二维线段树(树套树) [代码] #include<bits/stdc++.h> using namespace std; #define MAXN 8 ...
- 【转】创建和使用ANDROID LIBRARY工程
原文网址:http://www.cnblogs.com/Greenwood/archive/2011/06/19/2084499.html 摘要: 创建library供多个工程共享代码.资源是非常常见 ...
- 动态编译c#脚本(把c#当作脚本执行)
csscript动态编译C#脚本 This document contains information about the CLR based scripting system CS-Script ( ...
- 【重要】Selenium2+python自动化44-元素定位参数化(find_element)
转:https://www.cnblogs.com/yoyoketang/p/6551274.html 前言 元素定位有八种方法,这个能看到这一篇的小伙伴都知道了,那么有没有一种方法,可以把八种定位合 ...
- scrollerView 滚动的时候改变 scrollerView 的背景色代码
要实现点击电池条的时候立即回到页面的顶部的时候注意: 只有当一个主控制器有一个scrollview 并把这个属性设置为yes,其他的scrollview.scrollsToTop = NO 这样才会响 ...
- 查看mysql状态
命令:mysqladmin -uroot -p -h172.16.0.20 status Uptime: 14317755 Threads: 61 Questions: 187732924 Sl ...
- 任务13:在Core Mvc中使用Options
13 新建Controllers文件夹,在里面添加HomeController控制器 新建Views文件夹,再新建Home文件夹.再新建Index.cshtml的视图页面 注释上节课的代码,否则我们的 ...
- ORACLE PL/SQL 实例精解之第二章 通用编程语言基础
通用编程语言基础 2.1PL/SQL编程基础 1. 字符类型:支持四中类型字符:字母,数字,符号和空格,组合一个或多个字符就会创建一个词汇单元 2. 词汇单元 a.标识符必须以字符开头 b.保留字是p ...
- android 四大组件详解
这个文章主要是讲Android开发的四大组件,本文主要分为 一.Activity详解二.Service详解三.Broadcast Receiver详解四.Content Provider详解外加一个重 ...