您的位置:首页 > 其它

hdu 5726 GCD 倍增+ 二分

2016-07-20 19:45 447 查看
题目链接

给n个数, 定义一个运算f[l,r] = gcd(al, al+1,....ar)。 然后给你m个询问, 每次询问给出l, r。 求出f[l, r]的值以及有多少对l', r' 使得f[l, r] = f[l', r']。

第一个很简单, 用倍增的思想就可以了。

然后是第二个, 我们枚举每一个左端点i, 显然f[i, j]是只降不增的。 那么我们可以二分找到所有使得f[i, j]下降的值j。 因为gcd每次至少变为原来的二分之一, 而ai最大为1e9. 所以最多只有log2(1e9)个这样的点。 我们都找出来然后放到map里就可以了。 具体看代码

#include <bits/stdc++.h>

using namespace std;
#define ll long longint n;
const int maxn = 1e5+5;
int a[maxn], f[maxn][20], mm[maxn];
map <int, ll> mp;
int gcd(int a, int b)
{
return b?gcd(b, a%b):a;
}
void initrmq()
{
mm[0] = -1;
for(int i = 1; i <= n; i++) {
mm[i] = ((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
}
for(int j = 1; j < 17; j++) {
for(int i = 1; i + (1<<j)-1 <= n; i++) {
f[i][j] = gcd(f[i][j-1], f[i+(1<<(j-1))][j-1]);
}
}
}
int query(int l, int r)
{
int k = mm[r-l+1];
return gcd(f[l][k], f[r-(1<<k)+1][k]);
}
void pre()
{
for(int i = 1; i <= n; i++) {
int g = f[i][0];
int L = i, tmp;
while(L <= n) {
int l = L, r = n;
while(l <= r) {
int mid = l+r>>1;
if(query(i, mid) == g) {
tmp = mid;
l = mid+1;
} else {
r = mid-1;
}
}
mp[g] += (tmp-L+1);
L = tmp+1;
g = gcd(g, f[L][0]);
}
}
}
int main()
{
int t, m, l, r;
cin>>t;
for(int casee = 1; casee <= t; casee++) {
cin>>n;
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
f[i][0] = a[i];
}
mp.clear();
initrmq();
pre();
cin>>m;
printf("Case #%d:\n", casee);
for(int i = 0; i < m; i++) {
scanf("%d%d", &l, &r);
int ans = query(l, r);
printf("%d %lld\n", ans, mp[ans]);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: