題意: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;
}