题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=5869
题目:
给定序列和要查询的区间[L,R],求[L,R]内有多少个不同的区间gcd
解题思路:
(1)遍历序列,预处理出以a[i]为结尾的区间的gcd并记录,注意,有多个以a[i]为结尾的区间gcd相同,取左边界最大的那个,这样在用线段树求不同区间gcd的数目时,就避免了-1和+1的更新,节省时间和空间。为了取左边界最大的那个,可以倒序处理,即依次求[i,i] [i-1,i] [i-2,i]...[1,i]的区间gcd,如果当前求的gcd和上次求的gcd相同,则不更新这个gcd对应的左边界。
尝试自己写代码遇到的问题:不能用map<int,int> gd[maxn],因为map自动按照key值从小到大排序,影响结果,可以用pair和vector。此外,也不能用map中的key来存【x,i】的左边界x,如果这样的话在预处理时实践复杂度就达到了(n+1)*n/2, 会超时。
正确的处理方法:
vector<pair<int, int> > gd[maxn];//gd[i]中记录的是以a[i]为末尾的所有的gcd,<key,value>,表示【value,i】的区间gcd为key
void getIntervalGcd()
{
for(int i = 1; i <= n; i++)
{
int x = a[i], y = i;
gd[i].push_back(make_pair(x, y));
for(int j = 0; j < gd[i-1].size(); j++)
{
int tmp = Gcd(gd[i-1][j].first, a[i]);
if(tmp != x)
{
x = tmp, y = gd[i-1][j].second;
gd[i].push_back(make_pair(x, y));
}
}
}
}
补充一点:参照HDU5726 的第一种解题方法,可以求出序列中所有区间的gcd,但这个记录方法在本题中会MLE,并不适用
map<int, int> gc[maxn];//gc[i]中存放的(key,value)表示【key,i】的区间gcd为value
void getIntervalGcd()
{
gc[1][1] = a[1];
for(int i = 2; i <= n; i++)
{
gc[i][i] = a[i];
for(auto it = gc[i-1].begin(); it != gc[i-1].end(); it++)
{
int tmp = Gcd(it->second, a[i]);
gc[i][it->first] = tmp;
}
}
}
(2)参考洛谷P1972的思路,求区间内不同的gcd数目
ac代码:
#include <bits/stdc++.h>
using namespace std;
#define maxn 100010
#define lowbit(x) ((x) & (-x))
int n, q;
int c[maxn],a[maxn],ans[maxn];
vector<pair<int, int> > gd[maxn];//gd[i]中记录的是以a[i]为末尾的所有的gcd,<key,value>,表示【value,i】的区间gcd为key
map<int,int> last;
struct node {
int l,r,no;
friend bool operator < (node a, node b)
{
return a.r == b.r ? a.l < b.l : a.r < b.r;
}
}ask[maxn];
int Gcd(int a, int b)
{
return b == 0 ? a : Gcd(b, a % b);
}
void update(int x,int v)
{
for(int i = x; i <= n; i += lowbit(i))
c[i] += v;
}
int query(int x)
{
int sum=0;
for(int i = x; i > 0; i -= lowbit(i))
sum += c[i];
return sum;
}
void getIntervalGcd()
{
for(int i = 1; i <= n; i++)
{
int x = a[i], y = i;
for(int j = 0; j < gd[i-1].size(); j++)
{
int tmp = Gcd(gd[i-1][j].first, a[i]);
if(tmp != x)
{
gd[i].push_back(make_pair(x, y));
x = tmp, y = gd[i-1][j].second;
}
}
gd[i].push_back(make_pair(x, y));
}
}
void init()
{
last.clear();
for(int i = 1; i <= n; i++)
gd[i].clear();
memset(c, 0, sizeof(c));
}
int main()
{
//freopen("/Users/zhangkanqi/Desktop/11.txt", "r", stdin);
while(~scanf("%d %d",&n, &q))
{
init();
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
getIntervalGcd();
for(int i = 1; i <= q; i++)
{
scanf("%d %d", &ask[i].l, &ask[i].r);
ask[i].no = i;
}
sort(ask + 1, ask + 1 + q);
int k = 1;//ask的下标从1开始
for(int i = 1; i <= n; i++)
{
for(auto it = gd[i].begin(); it != gd[i].end(); it++)
{
int gcdValue=it->first, pos=it->second;
if(last[gcdValue])//这个gcd的值之前访问过
update(last[gcdValue], -1);
update(pos, 1);
last[gcdValue] = pos;
}
while(ask[k].r == i)
{
ans[ask[k].no] = query(ask[k].r) - query(ask[k].l - 1);
k++;
}
}
for(int i = 1; i <= q; i++)
printf("%d\n", ans[i]);
}
return 0;
}
【参考博客】: