ZOJ3800 GCD+离线线段树

Calculation

Time Limit: 5 Seconds       Memory Limit: 262144 KB

You are given a sequence {A0A1, ..., AN-1} and an integer set called GS. Defined a function called G(LR) on A, where G(LR) = GCD(Ai) (L ≤ iR) that is the greatest common divisor of all the integers in the subsequence {ALAL+1, ..., AR-1}.

Now There're several questions for you. Give you three integers LR and g, where g is an integer in GS. You need to calculate how many integer pairs (lr) satisfy the condition that G(lr) = g and L ≤ l < r ≤ R.

Input

Input will consist of multiple test cases. The first line of each case contains three integers NMQ (1≤ NQ≤ 100000, 1 ≤ M ≤ 50), separated by spaces. The second line contains N integers, A0A1, ..., AN-1 (1≤ Ai≤ 100000). The third line contains M integers, indicating the set GS. Every integer in GS is positive and less than 2000. For the next Q line, each line contains three integers, LRg (0≤ LR≤ Ng∈ GS).

Output

For each case, output Q lines. Each line contains an integer, indicating the answer for the query.

Sample Input
4 4 4
1 2 3 4
1 2 3 4
0 4 1
0 4 2
0 4 3
0 4 4
Sample Output
7
1
1
1

Author:  LIN, Xi

Source: ZOJ Monthly, August 2014


题目大意:给你N个数,和一个大小为M的集合,有Q组询问,每一组都询问[L,R-1]的区间的子区间GCD值为g的有多少个,g是集合中的一个数。


解题思路:

首先,注意到M只有50,可以预处理。

然后,GCD对于区间有非增的性质,那么对于数组A中的一个数Ai,以i为左端点的子区间GCD=g的是有范围的,我们可以维护这个区间,不妨设为[L,R],

L作为第一个使得区间[i,L]的GCD为g的下标,即min{x|GCD(i,x)==g}

R作为最右边是g的倍数的下标的后一位,即max{x|A[x]%g==0}

那么范围只有以下几种情况:

1. Ai%g!=0   范围不存在

2. Ai%g==0   L不存在,R存在

3. Ai%g==0   L,R都存在 

通过区间GCD删除一个数后不会小于原来的值,我们可以发现L,R具有单调性

那么可以用扫描线处理出以每个点为左端点,GCD为M中的一个数的符合的范围

有了这个下面就简单多了。

离线排序,通过M把询问分类,对于每一类按询问区间的右端点排序,把大于右端点的点的范围全部插入线段数,然后询问区间左端点即可


#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define maxn 100010
#define max(a,b) (a>=b ? a:b)
#define ll long long
using namespace std;
int n,m,q;
int v[maxn],s[maxn],f[maxn],LL[55][maxn],RR[55][maxn];
ll cc[55][maxn],ans[maxn];
struct Query{
    int l,r,g,x;
    friend bool operator<(Query a,Query b){
        if(a.g!=b.g) return a.g<b.g;
        return a.l>b.l;
    }
}Q[maxn];
int Log[maxn];
void init(){
    for(int i=1;i<maxn;i++) Log[i]=log(i*1.0)/log(2.0);
}
void read(){
    for(int i=1;i<=n;i++) scanf("%d",&v[i]);
    for(int i=1;i<=m;i++) scanf("%d",&s[i]);
    sort(s+1,s+m+1);
    for(int i=1;i<=m;i++) f[s[i]]=i;
    for(int i=1;i<=q;i++) scanf("%d%d%d",&Q[i].l,&Q[i].r,&Q[i].g),Q[i].x=i;
}
int dp[maxn][20];
void RMQ(){
    memset(dp,0,sizeof dp);
    for(int i=1;i<=n;i++) dp[i][0]=v[i];
    for(int i=1;i<20;i++){
        for(int j=1;j+(1<<i>>1)<=n;j++){
            dp[j][i]=__gcd(dp[j][i-1],dp[j+(1<<i>>1)][i-1]);
        }
    }
}
int query_RMQ(int l,int r){
    int h=Log[r-l+1];
    return __gcd(dp[l][h],dp[r-(1<<h)+1][h]);
}
struct tree{
    int l,r;
    ll s,lz;
}a[maxn<<2];
void build(int l,int r,int k){
    a[k].l=l,a[k].r=r,a[k].s=0,a[k].lz=0;
    if(l<r){
        int mid=(l+r)>>1;
        build(l,mid,k<<1);
        build(mid+1,r,k<<1|1);
    }
}
void pushdown(int k){
    if(a[k].lz){
        a[k<<1].s+=(a[k<<1].r-a[k<<1].l+1)*a[k].lz;
        a[k<<1|1].s+=(a[k<<1|1].r-a[k<<1|1].l+1)*a[k].lz;
        a[k<<1].lz+=a[k].lz;
        a[k<<1|1].lz+=a[k].lz;
        a[k].lz=0;
    }
}
void insert(int l,int r,int k){
    if(l<=a[k].l&&a[k].r<=r){
        a[k].s+=(r-l+1);
        a[k].lz++;
    }else{
        pushdown(k);
        int mid=(a[k].l+a[k].r)>>1;
        if(r<=mid) insert(l,r,k<<1);
        else if(l>mid) insert(l,r,k<<1|1);
        else insert(l,mid,k<<1),insert(mid+1,r,k<<1|1);
        a[k].s=a[k<<1].s+a[k<<1|1].s;
    }
}
ll query(int l,int r,int k){
    if(l<=a[k].l&&a[k].r<=r){
        return a[k].s;
    }else{
        pushdown(k);
        int mid=(a[k].l+a[k].r)>>1;
        if(r<=mid) return query(l,r,k<<1);
        else if(l>mid) return query(l,r,k<<1|1);
        else return query(l,mid,k<<1)+query(mid+1,r,k<<1|1);
    }
}
void solve(){
    RMQ();
    memset(cc,0,sizeof cc);
    for(int i=1;i<=m;i++){
        int L=1,R=1;
        for(int j=1;j<=n;j++){
            L=max(L,j),R=max(R,j);
            int GCD;
            if(v[j]%s[i]==0){
                GCD=query_RMQ(j,L);
                if(GCD!=s[i]){
                    while(L<=n&&__gcd(GCD,v[L])!=s[i]&&GCD!=1) GCD=__gcd(GCD,v[L++]);
                }
                while(R<=n&&v[R]%s[i]==0) R++;
            }
            LL[i][j]=L,RR[i][j]=R;
            if(GCD!=s[i]) L=1;
        }
    }
    /*for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++){
            cout<<i<<" "<<j<<" "<<LL[i][j]<<" "<<RR[i][j]<<endl;
        }
    }*/
    sort(Q+1,Q+q+1);
    for(int i=1,j;i<=q;i++){
        if(Q[i].g!=Q[i-1].g) build(1,n,1),j=n;
        int z=f[Q[i].g];
        Q[i].l++;
        while(j>=Q[i].l){
            if(RR[z][j]>LL[z][j]) insert(LL[z][j],RR[z][j]-1,1);
            j--;
        }
        ans[Q[i].x]=query(Q[i].l,Q[i].r,1);
    }
    for(int i=1;i<=q;i++){
        printf("%lld\n",ans[i]);
    }
}
int main(){
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    init();
    while(~scanf("%d%d%d",&n,&m,&q)){
        read();
        solve();
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值