您的位置:首页 > 其它

HDU 5726 GCD[2016多校#1]

2016-10-28 00:49 239 查看

HDU 5726 GCD

HDU 5726 GCD
题意

解题思路

代码

题意

给连续N个数,求[l,r]区间的GCD,给出M次查询,查出和[l,r]的GCD相同的区间有多少。

N、M都是10W级别。

解题思路

记得当时分析出来从i开始的一串GCD单调不减,GCD为1后一直为1,简单贪心了终结为1的位置,显然超时。

那么正确解法为用RMQ维护[l,r]的GCD,然后用二分查找,依次查出从左端点i开始的具有相同GCD的右端点范围,然后用map累加起来。

借鉴这个博客的思路。

代码

#include <cstdio>
#include <algorithm>
#include <map>
#include <cmath>
using namespace std;

int f[100010][18];
int a[100010];
int n, m;

int __gcd(int a, int b)
{
int t;
while(b)
{
t = a % b;
a = b;
b = t;
}
return a;
}

//动规思想,二分线段树思想,O(nlogn)计算连续段的GCD
//f[i][j]表示i开始2^j个的GCD
void rmq()
{
for (int i = 1; i <= n; ++i)
{
f[i][0] = a[i];
}
for (int i = 1; i < 18; ++i)
{
for (int j = 1; j + (1 << i) - 1 <= n; ++j)
{
f[j][i] = __gcd(f[j][i - 1], f[j + (1 << (i - 1))][i - 1]);
}
}
}

map<int, long long> mp;
//利用从i开始的一组数字的GCD随着数字增多单调不增,二分计算GCD数目
//插入Map

int findgcd(int l, int r)
{
int k = (int)log2((double)(r - l + 1));
return __gcd(f[l][k], f[r - (1 << k) + 1][k]);
}

void setTable()
{
mp.clear();
for (int i = 1; i <= n; ++i)//从i开始
{
int curgcd = f[i][0];
int j = i;
while(j <= n)
{
int l = j, r = n;
while(l < r)
{
int mid = (l + r + 1) >> 1;
if (findgcd(i, mid) == curgcd) l = mid;
else r = mid - 1;
}
mp[curgcd] += l - j + 1;
j = l + 1;//计算一个更小的GCD
curgcd = findgcd(i, j);
}
}
}

int main(int argc, char const *argv[])
{
int T;
scanf("%d", &T);
for(int cas = 1; cas <= T; ++cas)
{
printf("Case #%d:\n", cas);
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
{
scanf("%d", &a[i]);
}
rmq();
setTable();
scanf("%d", &m);
for (int i = 0; i < m; ++i)
{
int l, r;
scanf("%d %d", &l, &r);
int curgcd = findgcd(l, r);
printf("%d %I64d\n", curgcd, mp[curgcd]);
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  RMQ ACM 二分 多校 GCD