题意:有一个含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. 关于O_DIRECT的那些事儿

    很久之前落下的坑,一直没怎么记录-.- 一般地,如果在Linux内核中读写一个文件,其IO流程都需要经过Kernel内的page cache层次,如果程序员若想要使用自己开发的缓存系统,那么就可以在打 ...

  2. poj 3461 Oulipo(kmp统计子串出现次数)

    题意:统计子串出现在主串中的次数 思路:典型kmp #include<iostream> #include<stdio.h> #include<string.h> ...

  3. 如何在chrome上设置Bing为默认搜索引擎,在设置中无法直接设置

  4. JRE System Library 与Java EE Libraries的区别

    JRE System Library是只要做java开发都需要的完整的.标准的库.  Java EE5 Libraries只是java三个方向中做java EE所需要的库.如果做Web方面的开发的话就 ...

  5. codeforces 690D1 D1. The Wall (easy)(dfs)

    题目链接: D1. The Wall (easy) time limit per test 0.5 seconds memory limit per test 256 megabytes input ...

  6. C++ main函数中参数argc和argv含义及用法

    argc 是 argument count的缩写,表示传入main函数的参数个数: argv 是 argument vector的缩写,表示传入main函数的参数序列或指针,并且第一个参数argv[0 ...

  7. Watir: 当出现错误提示AutoItX3.dll 没有注册的时候,该怎么处理?

    对于Ruby 1.8版本,以管理员身份运行命令行窗口,输入Regsvr32 AutoItX3.dll路径即可.对于1.9 版本,路径与1.8版本是不同的,我们可以进入Ruby安装目录下,搜索AutoI ...

  8. MYSQL数据库学习----索引和触发器

    一:索引 索引是创建在数据库表上,其作用是提高对表中数据的查询速度. 假设数据库中有一张1000条记录的表格,如果没有创建索引的话,用户想通过查询条件查询,实际上是把整个数据库中1000条记录都读取一 ...

  9. bzoj 2131: 免费的馅饼【dp+树状数组】

    简单粗暴的dp应该是把馅饼按时间排序然后设f[i]为i接到馅饼能获得的最大代价,转移是f[i]=max(f[j])+v[i],t[j]<=t[i],2t[i]-2t[j]>=abs(p[i ...

  10. 【CodeForces - 501B 】Misha and Changing Handles(map)

    Misha and Changing Handles CodeForces原题是英文,这里就直接上中文好了,翻译不是太给力,但是不影响做题 ^▽^ Description  神秘的三角洲里还有一个传说 ...