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 ...
随机推荐
- HTTPS站点搭建教程:Win7/Windows Server 2008R2
本文将由笔者为各位读者介绍在win7/windows server 2008R2环境下使用SSL加密协议建立WWW站点的全过程:https SSL证书安装的搭建以及本地测试环境. 要想成功架设SSL安 ...
- web项目中url-pattern改成'/'后,js、css、图片等静态资源(404)无法访问问题解决办法
感谢http://blog.csdn.net/this_super/article/details/7884383的文章 1.增加静态资源url映射 如Tomcat, Jetty, JBoss, Gl ...
- Android Studio 1.5.1
Android Studio 1.5.1 December 3rd, 2015: For information on what's new in 1.5.1, see the release ann ...
- 【旧文章搬运】修改PEB,断链隐藏模块成功
原文发表于百度空间,2008-7-26========================================================================== 继续实践之前 ...
- 整合ssh的时候出现空指针java.lang.NullPointerException
转自:https://blog.csdn.net/koudailidexiaolong/article/details/9468857 HTTP Status 500 - type Exception ...
- c++ 头文件 及 sort 和 vector简单介绍
c++ sort :http://www.16kan.com/post/997260.html http://wenku.baidu.com/view/e064166daf1ffc4ffe47ac6 ...
- lightoj1079【背包】
题意: 哈利波特抢银行... 给出n个银行,每个银行有a[i]百万和b[i]的风险.然后再给一个风险值P,不能超过P. 思路: 如果告诉你概率的小数的位数,可能这个就会不一样了... 慨率的计算,风险 ...
- 当打开一个.h或.cpp文件时, Solution Explorer就自动展开文件所在的目录
当打开一个.h或.cpp文件时, Solution Explorer就自动展开文件所在的目录: 如果不想展开: Tools -> Options -&g ...
- bzoj 3676: [Apio2014]回文串【回文自动机】
回文自动机板子 或者是SAM+manacher+倍增,就是manacher求本质不同回文串(让f++的串),然后在SAM倍增查询对应点出现次数 #include<iostream> #in ...
- C#读取大文件
有些时候需要读取文件,小文件的时候效率的影响可以忽略,但是当文件上M,上G的时候,这个时候,效率问题就非常重要了,下面将对一个3G的文件,用C#的方式读取,对比效率的影响. 1. FileStream ...