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 ...
随机推荐
- laravel打印原生语句
laravel中快捷方便的打印语句的方法步骤一:DB::connection()->enableQueryLog();$data["banner"] = WebsiteBan ...
- Redmine 数据库连接错误
/******************************************************************** * Redmine 数据库连接错误 * 说明: * Open ...
- Sorting a Three-Valued Sequence
链接 分析:首先我们先对其进行排序,并看排序以后有多少个元素顺序是对的,接着我们看有多少个元素经过一次交换可以得到的,最后剩下的元素就是经过两次交换可以得到的了. /* PROB:sort3 ID:w ...
- [TJOI2012]防御
https://www.zybuluo.com/ysner/note/1332539 题面 戳我 解析 一道挺棒棒的线段树. 显然一次伤害到来时我们要先看看区间内哪些点的护甲没了. 这个可以通过维护区 ...
- PostgreSQ 连接问题 FATAL: no pg_hba.conf entry for host
PostgreSQ数据库为了安全,它不会监听除本地以外的所有连接请求,当用户通过JDBC访问是,会报一些如下的异常: org.postgresql.util.PSQLException: FATAL: ...
- 是时候开刷NOI了
整天挨着毛爷爷,压力好大.. 看毛爷爷即将炖完NOI,我的确也该刷了 原则是从头到尾自己想(虽然看了一次题解),可以不A掉. NOI2009 day1: T1 题目略神,我还是不讲了...(就这题我W ...
- web安全之XSS攻击原理及防范
阅读目录 一:什么是XSS攻击? 二:反射型XSS 三:存储型XSS 四:DOM-based型XSS 五:SQL注入 六:XSS如何防范? 1. cookie安全策略 2. X-XSS-Protect ...
- Springboot 配置 application.yml 连接MySQL数据库
1.在pom.xml的<dependencies></dependencies>标签中中加入以下依赖 <dependency> <groupId>org ...
- JS判断字符串长度(英文占1个字符,中文汉字占2个字符)
//计算字符串长度(英文占1个字符,中文汉字占2个字符) 方法一: String.prototype.gblen = function() { var len = 0; for (var i=0; i ...
- Qt样式表之一:Qt样式表和盒子模型介绍
一.Qt样式表介绍 Qt样式表是一个可以自定义部件外观的十分强大的机制,可以用来美化部件.Qt样式表的概念.术语和语法都受到了HTML的层叠样式表(Cascading Style Sheets, CS ...