题意:有一个含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 (技巧,莫队算法)的更多相关文章

  1. 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 ...

  2. hdu 5381 The sum of gcd(线段树+gcd)

    题目链接:hdu 5381 The sum of gcd 将查询离线处理,依照r排序,然后从左向右处理每一个A[i],碰到查询时处理.用线段树维护.每一个节点表示从[l,i]中以l为起始的区间gcd总 ...

  3. hdu 5381 The sum of gcd

    知道对于一个数列,如果以x为左(右)端点,往右走,则最多会有log(a[x])个不同的gcd,并且有递减性 所以会分成log段,每一段的gcd相同 那我们可以预处理出对于每一个位置,以这个位置为左端点 ...

  4. 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 ...

  5. 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 ...

  6. HDU 4676 Sum Of Gcd 【莫队 + 欧拉】

    任意门:http://acm.hdu.edu.cn/showproblem.php?pid=4676 Sum Of Gcd Time Limit: 10000/5000 MS (Java/Others ...

  7. 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 ...

  8. 【HDU4676】Sum Of Gcd(莫队+欧拉函数)

    点此看题面 大致题意: 多组询问,求\(\sum_{i=L}^R\sum_{j=i+1}^Rgcd(i,j)\). 推式子 这道题我们可以考虑,每个因数\(d\)被统计答案的次数,肯定与其出现次数有关 ...

  9. hdu 4358 Boring counting 离散化+dfs序+莫队算法

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4358 题意:以1为根节点含有N(N <= 1e5)个结点的树,每个节点有一个权值(weight ...

随机推荐

  1. .NET 4.0 System.Threading.Tasks学习笔记

    由于工作上的需要,学习使用了System.Threading.Tasks的使用,特此笔记下来. System.Threading.Tasks的作用: Tasks命名空间下的类试图使用任务的概念来解决线 ...

  2. Fleck websocket官方事例

    Fleck websocket官方事例 server: using Fleck;using System;using System.Collections.Generic;using System.L ...

  3. 设置linux服务器下开放端口

    查询 netstat -anp  所有开放端口信息 二.关闭端口号: iptables -A OUTPUT -p tcp --dport 端口号-j DROP 三.打开端口号: iptables -A ...

  4. 前端CSS规范整理

    一.文件规范 1.文件均归档至约定的目录中. 具体要求通过豆瓣的CSS规范进行讲解: 所有的CSS分为两大类:通用类和业务类.通用的CSS文件,放在如下目录中: 基本样式库 /css/core 通用U ...

  5. 并不对劲的多项式求ln,exp

    ln 解释 设\(g(x)=ln(f(x))\),两边同时求导,则有:\(g'(x)=ln'(f(x))*f'(x)=f^{-1}(x)*f'(x)\)(1) 因为\(f(x)\)是个多项式,所以设\ ...

  6. CoderForces343D:Water Tree(dfs序+线段树&&特殊处理)

    Mad scientist Mike has constructed a rooted tree, which consists of n vertices. Each vertex is a res ...

  7. iOS 观察者模式(KVO)的简单使用

    KVO的全称是Key-Value Observing,它实现了一种机制,对所关心的属性对象添加观察者,当属性值发生变化时会得到通知,我们可以对变化做相应的处理.看过设计模式的同学应该知道,这是一种典型 ...

  8. LRU原理和Redis实现——一个今日头条的面试题

    看了评论,发现有些地方有问题,更新了图和一些描述,希望可以更清晰一些,也欢迎关注,还会有干货文章 -------- 很久前参加过今日头条的面试,遇到一个题,目前半部分是如何实现 LRU,后半部分是 R ...

  9. 哈希表的C实现(二)

    上次大致分析了一下哈希表的链地址法的实现,今天来分析一下另一种解决哈希冲突的做法,即为每个Hash值,建立一个Hash桶(Bucket),桶的容量是固定的,也就是只能处理固定次数的冲突,如104857 ...

  10. Android开发中几种有用的的日历控件实现

    我们大家都知道,在Android平台3.0中才新增了日历视图控件,可以显示网格状的日历内容,那么对于3.0以下的版本要使用日历控件只能借助第三方,目前用的最多的是CalendarView. 先简单介绍 ...