hdu5869Different GCD Subarray Query

链接:http://acm.hdu.edu.cn/showproblem.php?pid=5869

题意:给定一个a数组,每次询问一个区间[l,r]求这个区间内所有子区间的gcd的种类。

分析:很容易想到,当我们固定右端点时在L减小的情况下gcd是递减的且最多形成loga[i]个不同的gcd,当我们固定右端点时对于不同的gcd有一个最大左端点(即L至少要到这个才能在L~R这个区间中能形成这个gcd)。我们将所有询问离线按右端点从小到大排序然后我们将R向右移动,根据之前的结论我们知道从a[i]移动到a[i+1]是最多有loga[i+1]个gcd的最大左端点会发生变化,那么我们用个树状数组维护下就行了。那么对于每次询问因为R已经移动到了q[i].r那么我们只需要统计有多少个gcd的最大左端点是>=q[i].l的就行啦。

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<bitset>
#include<math.h>
#include<vector>
#include<string>
#include<stdio.h>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=100010;
const int mod=1000000007;
const int MOD1=1000000007;
const int MOD2=1000000009;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=1000000007;
const int INF=1000000010;
const ll MAX=1ll<<55;
const double eps=1e-5;
const double inf=~0u>>1;
const double pi=acos(-1.0);
typedef double db;
typedef unsigned int uint;
typedef unsigned long long ull;
struct node {
    int l,r,id;
}q[N];
int n,a[N][20],bit[20],ans[N],f[N];
int l[N],gl[N][20],wl[N][20],w[10*N];
int gcd(int x,int y) {
    return !y ? x:gcd(y,x%y);
}
int getgcd(int x,int y) {
    int z=(int)log2(y-x+1);
    return gcd(a[x][z],a[y-bit[z]+1][z]);
}
bool cmd(node x,node y) {
    return x.r<y.r;
}
void deal() {
    int i,j,G,ll,rr,mid;
    for (j=1;j<20;j++)
        for (i=1;i<=n;i++)
        if (i+bit[j]-1>n) break ;
        else a[i][j]=gcd(a[i][j-1],a[i+bit[j-1]][j-1]);
    for (i=1;i<=n;i++) {
        l[i]=0;wl[i][0]=i+1;
        while (wl[i][l[i]]>1) {
            G=getgcd(wl[i][l[i]]-1,i);
            ll=0;rr=wl[i][l[i]]-1;mid=(ll+rr)>>1;
            while (ll+1<rr)
            if (getgcd(mid,i)==G) { rr=mid;mid=(ll+rr)>>1; }
            else { ll=mid;mid=(ll+rr)>>1; }
            l[i]++;gl[i][l[i]]=G;wl[i][l[i]]=rr;
        }
    }
}
void add(int x,int y) {
    if (x<=0) return ;
    for (;x<=n;x+=x&-x) f[x]+=y;
}
int getsum(int x) {
    int ret=0;
    for (;x;x-=x&-x) ret+=f[x];
    return ret;
}
void updata(int x) {
    for (int i=0;i<l[x];i++)
    if (wl[x][i]-1>w[gl[x][i+1]]) {
        add(w[gl[x][i+1]],-1);add(wl[x][i]-1,1);w[gl[x][i+1]]=wl[x][i]-1;
    }
}
int main()
{
    int i,j,m,R;
    for (i=0;i<20;i++) bit[i]=1<<i;
    while (scanf("%d%d", &n, &m)!=EOF) {
        for (i=1;i<=n;i++) scanf("%d", &a[i][0]);
        for (i=1;i<=m;i++) scanf("%d%d", &q[i].l, &q[i].r),q[i].id=i;
        deal();sort(q+1,q+m+1,cmd);
        memset(f,0,sizeof(f));
        memset(w,0,sizeof(w));
        for (R=0,i=1;i<=m;i++) {
            while (R<q[i].r) R++,updata(R);
            ans[q[i].id]=getsum(q[i].r)-getsum(q[i].l-1);
        }
        for (i=1;i<=m;i++) printf("%d\n", ans[i]);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值