您的位置:首页 > 其它

HDU 5726 GCD(RMQ+二分)

2017-10-11 21:44 441 查看
http://acm.split.hdu.edu.cn/showproblem.php?pid=5726

题意:
给出一串数字,现在有多次询问,每次询问输出(l,r)范围内所有数的gcd值,并且输出有多少数量区间的gcd值等于该gcd值。

思路:

第一问的话可以用线段树或RMQ来解决,RMQ的话简单点。

有意思的是第二问,假设我们现在固定左端点,那么往右端扩大区间时,gcd单调不增,并且每次至少减少一倍,所以我们可以二分枚举。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
#include<stack>
#include<queue>
#include<cmath>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
typedef pair<int,int> pll;
const int INF = 0x3f3f3f3f;
const int maxn = 1e5 + 5;

int n;
int a[maxn];
int d[maxn][50];

map<int,ll> ans;

int gcd(int a, int b)
{
return b==0?a:(gcd(b,a%b));
}

void init()
{
for(int i=0;i<n;i++)  d[i][0]=a[i];
for(int j=1;(1<<j)<=n;j++)
for(int i=0;i+(1<<j)-1<n;i++)
d[i][j]=gcd(d[i][j-1],d[i+(1<<(j-1))][j-1]);
}

int query(int L, int R)
{
int k = 0;
while((1<<(k+1)) <= R-L+1)  k++;
return gcd(d[L][k],d[R-(1<<k)+1][k]);
}

int main()
{
//freopen("in.txt","r",stdin);
int T;
int kase = 0;
scanf("%d",&T);
while(T--)
{
printf("Case #%d:\n",++kase);
scanf("%d",&n);
for(int i=0;i<n;i++)  scanf("%d",&a[i]);
ans.clear();
init();
for(int i=0;i<n;i++)
{
int j=i, g=a[i];
while(j<n)
{
int l=j,r=n-1;
while(l<r)
{
int mid=(l+r)>>1;
if(query(l,r)==g)  l=mid+1;
else r=mid-1;
}
ans[g]+=l-j+1;
j=l+1;
g=gcd(g,a[j]);
}

}
int q; scanf("%d",&q);
while(q--)
{
int l,r;
scanf("%d",&l);scanf("%d",&r);
int g=query(l-1,r-1);
printf("%d %lld\n",g,ans[g]);
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: