HDU 5869区间CGD不同种类数---树状数组+map统计区间不同种类数(离线)
http://acm.hdu.edu.cn/showproblem.php?pid=5869
题意:已知一个数组,Q个查询,问你区间子区间的不同GCD种类数
思路:先考虑一个数组区间不同个数,可以使用离线的树状数组实现,具体是对查询右端点进行排序,依次转移解决问题。
首先我们考虑固定右端点的查询区间不同数字,其实我们可以在每个数字出现的最右位置记录一下就可以了,统计起来就是sum[r]-sum[l]
这样子考虑右区间移动,就要改变那个数的最右位置,显然可以用一个map来维护每个数字出现的最右位置。
回到这个题目,显然一个点不只是一个数字,我们按照之前多校的一个处理方法,处理出来每个点作为右端点的子区间的GCD值(套路,而且一个点的不会很多,大致logAi个,用一个vector数组维护爽一下),这样子每个点就是一个数组了,但是统计的时候又不是区间所有点数组的不同数字个数,但是也很好魔改,其实我们只需要每个GCD出现的最右距离就可以了,因为如果查询的L在这个L左面,自然会被算进去,如果不在,那么就无视,以后的查询可能会需要,还是继续用map维护就好了。
所以这道题就解决啦!关键还是学会树状数组加map来统计区间不同种类数的方法。
代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <map>
#include <vector>
using namespace std;
#define MAX 100005
const int INF=1e9+7;
int n,m;
int a[MAX];
int b[MAX];
int ans[MAX];
struct Q{
int r,l,id;
}q[MAX];
vector<pair<int,int> > G[MAX];
map<int,int> H;
int lowbit(int x){
return x & (-x);
}
void modify(int x,int add){
while(x<=MAX){
b[x]+=add;
x+=lowbit(x);
}
}
int get_sum(int x){
int ret=0;
while(x!=0){
ret+=b[x];
x-=lowbit(x);
}
return ret;
}
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
bool cmp(const Q &q1,const Q &q2){
if(q1.r!=q2.r)
return q1.r<q2.r;
else return q1.id<q2.id;
}
void F(int x){
G[x].push_back(make_pair(a[x],x));
int cnt=0;
for(int i=0;i<G[x-1].size();i++){
int gc=gcd(G[x-1][i].first,a[x]);
if(gc!=G[x][cnt].first){
G[x].push_back(make_pair(gc,G[x-1][i].second));
cnt++;
}
else{
// G[x][cnt].second=G[x-1][i].second;
}
}
}
void init(){
for(int i=0;i<=n;i++) G[i].clear();
H.clear();
memset(b,0,sizeof(b));
G[1].push_back(make_pair(a[1],1));
for(int i=2;i<=n;i++) F(i);
}
int main(){
int t;
while (scanf("%d%d",&n,&m)!=EOF){
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
init();
int l,r;
for(int i=0;i<m;i++){
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id=i;
}
sort(q,q+m,cmp);
int R=1;
for(int i=0;i<m;i++){
while(R<=q[i].r){
for(int j=0;j<G[R].size();j++){
if(H.count(G[R][j].first)!=0){
modify(H[G[R][j].first],-1);
}
H[G[R][j].first]=G[R][j].second;
modify(G[R][j].second,1);
}
R++;
}
ans[q[i].id]=get_sum(q[i].r)-get_sum(q[i].l-1);
}
for(int i=0;i<m;i++) printf("%d\n",ans[i]);
}
return 0;
}