题意:T组测试数据,每组数据给你n个数,m次询问每次询问包含两个数字l,r。每次查询[l,r]区间内的数的最大公约数。并输出整个序列中最大公约数与[l,r]最大公约数相等的组数。
解题方法:可以通过线段树来记录每段区间内的最大公约数,但是后来统计与[l,r]最大公约数相等的个数时,老是超时。最后也还是在网上查的代码。
但是后来看了也有用RMQ来解题的,后来想了想,RMQ比线段树查询起来可能效率更快。
附线段树ac代码:
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cmath>
#include <map>
using namespace std;
#define ll long long
const int maxn=1000010;
struct node{///线段树的存储结构
int l,r;
ll gcd;
}Tree[maxn<<2];
ll a[maxn];
ll gcd(ll a,ll b)
{
return b==0?a:gcd(b,a%b);
}
void pushup(int rt)
{
Tree[rt].gcd=gcd(Tree[rt*2].gcd,Tree[rt*2+1].gcd);
}
///建立线段树
void Build(int l,int r,int rt)
{
Tree[rt].l=l,Tree[rt].r=r;
if(l==r)
{
Tree[rt].gcd=a[l];
return ;
}
int mid=(l+r)/2;
Build(l,mid,rt*2);
Build(mid+1,r,rt*2+1);
pushup(rt);
}
///询问线段树
ll queryans(int L,int R,int rt)
{
if(L<=Tree[rt].l&&Tree[rt].r<=R)
{
return Tree[rt].gcd;
}
int mid=(Tree[rt].l+Tree[rt].r)/2;
ll ans=0;
if(L<=mid) ans=gcd(ans,queryans(L,R,rt*2));
if(mid<R) ans=gcd(ans,queryans(L,R,rt*2+1));
return ans;
}
int n;
map<ll,ll>ans;
map<ll,ll>mp1;//mp1代表以x[i]结尾的所有区间的gcd的个数
map<ll,ll>mp2;//临时变量
int main()
{
int T;
scanf("%d",&T);
int cas=1;
while(T--)
{
scanf("%d",&n);
ans.clear();
mp1.clear();
mp2.clear();
///输入数据
for(int i=1; i<=n; i++) scanf("%I64d",&a[i]);
Build(1,n,1);///建立线段树
mp1[a[1]]++;
ans[a[1]]++;
///将查询的ged的结果存到ans中
for(int i=2; i<=n; i++)
{
ll now=a[i];
mp2[now]++;
ans[now]++;
map<ll,ll>::iterator it;
for(it=mp1.begin(); it!=mp1.end(); it++)
{
int nex=gcd(now,it->first);
ans[nex]+=it->second;
mp2[nex]+=it->second;
}
mp1.clear();
for(it=mp2.begin(); it!=mp2.end(); it++)
{
mp1[it->first]=it->second;
}
mp2.clear();
}
int q;
printf("Case #%d:\n",cas++);
scanf("%d",&q);
///查询输出过程
while(q--)
{
int l,r;
scanf("%d%d",&l,&r);
ll temp=queryans(l,r,1);
printf("%I64d %I64d\n",temp,ans[temp]);
}
}
}
另外转载一下别的大神的RMQ代码:
RMQ统计预处理[l,r]内的gcd值
枚举左端点,随着右端点增大,gcd值越来越小,二分这些变小的节点,统计出线段的gcd值所作的贡献,map累加每个贡献的数量
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include<ctime>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<iomanip>
#include<cmath>
#define mst(ss,b) memset((ss),(b),sizeof(ss))
#define maxn 0x3f3f3f3f
#define MAX 1000100
///#pragma comment(linker, "/STACK:102400000,102400000")
typedef long long ll;
typedef unsigned long long ull;
#define INF (1ll<<60)-1
using namespace std;
ll a[100100];
ll mp[100100][65];
ll gcd(ll a,ll b){
return b==0?a:gcd(b,a%b);
}
int n,q;
void RMQ_init(){
for(int i=1;i<=n;i++) mp[i][0]=a[i];
int m=log(n)/log(2);
for(int i=1;i<=m;i++){
for(int j=n;j>=1;j--){
mp[j][i]=mp[j][i-1];
if(j+(1<<(i-1))<=n)
mp[j][i]=gcd(mp[j][i],mp[j+(1<<(i-1))][i-1]);
}
}
}
int query(int l,int r){
int m=log(r-l+1)/log(2);
return gcd(mp[l][m],mp[r-(1<<m)+1][m]);
}
map<int,ll>V;
int main(){
int T;
scanf("%d",&T);
for(int cas=1;cas<=T;cas++){
scanf("%d",&n);
V.clear();
for(int i=1;i<=n;i++) scanf("%I64d",&a[i]);
RMQ_init();
for(int i=1;i<=n;i++){
ll tmp=a[i];
int l=i,r=n,mid,ans=l,O=l;
while(tmp>1){
O=l;r=n;ans=-1;
while(l<=r){
mid=(l+r)/2;
if(query(i,mid)<tmp) r=mid-1;
else {
l=mid+1;
ans=mid;
}
}
if(tmp==1) break;
V[tmp]+=ans-O+1;
l=ans+1;
if(l>n) break;
tmp=query(i,l);
}
if(tmp==1){
V[1]+=n-l+1;
}
}
scanf("%d",&q);
printf("Case #%d:\n",cas);
while(q--){
int l,r;
scanf("%d%d",&l,&r);
ll x=query(l,r);
printf("%I64d %I64d\n",x,V[x]);
}
}
return 0;
}