区间 GCD

Posted 人间失格

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了区间 GCD相关的知识,希望对你有一定的参考价值。

区间 GCD
题目描述
最近 JC 同学刚学会 gcd,于是迷上了与 gcd 有关的问题。今天他又出了一道这样的题目,
想要考考你,你能顺利完成吗?
给定一个长度为 n 的字符串 s[1..n],串仅包含小写字母。对于区间 [l, r],你需要回答 s[l..r]
中有多少个长度为 3 的子序列组成了"gcd",即有多少组 (i, j, k) 满足 l ≤ i < j < k ≤ r, s[i] =
‘g‘, s[j] = ‘c‘, s[k] = ‘d‘。
输入格式
第一行为一个字符串 s。
第二行为一个整数 q,表示询问数量。
接下来 q 行,每行两个整数 l i , r i ,表示一组询问。
输出格式
输出共 q 行,表示每一组询问的答案。答案请对 2 31 取模后输出。
样例输入
glygshcyjcdzy
3
1 11
2 11
2 10
样例输出
4
2
0
数据规模与约定
对于 20% 的数据,n ≤ 300, q = 1。
对于 40% 的数据,n ≤ 300, q ≤ 300。
对于 70% 的数据,n ≤ 4000, q ≤ 4000。
对于 100% 的数据,n ≤ 80000, q ≤ 80000。
串仅包含小写字母。1 ≤ l i ≤ r i ≤ n。

 

题解:

  这个题目仔细想想其实是考试里最简单的一道题,但还是让我很有启发。

  首先,我们考虑维护区间的g,c,d,gc,cd,gcd的个数,怎么维护呢?

  g,c,d可以直接统计,如果用线段树做的话,gc的个数=ls的gc数+rs的gc+ls]的g数*rs的c数,cd数同理,然后gcd数了,gcd数=ls的gcd+rs的gcd+ls的gc*rs的d+ls的g*rs的cd;其实这样讲的话感觉就一点也不神奇,很理所当然了,但是至少启发我们线段树中可以理由递推关系记一些元素推出一些元素。当然返回的时候返回的是结构题。

 

代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#define ll long long
#define MAXN 80100
using namespace std;
int sum[MAXN],len,q;
ll ans=0,mod=(ll)1<<31;
char s[MAXN];
struct tree{
    ll g,c,d,gc,cd,gcd;
    int l,r;
}a[MAXN*4];

void pushup(int xv){
    int ls=xv*2,rs=xv*2+1;
    a[xv].g=a[ls].g+a[rs].g;
    a[xv].c=a[ls].c+a[rs].c;
    a[xv].d=a[ls].d+a[rs].d;
    a[xv].cd=(a[ls].cd+a[rs].cd+a[ls].c*a[rs].d)%mod;
    a[xv].gc=(a[ls].gc+a[rs].gc+a[ls].g*a[rs].c)%mod;
    a[xv].gcd=(a[ls].gcd+a[rs].gcd+a[ls].gc*a[rs].d+a[ls].g*a[rs].cd)%mod;
}

void build(int xv,int l,int r){
    if(l==r){
        a[xv].l=l,a[xv].r=r;
        a[xv].g=a[xv].d=a[xv].c=a[xv].gc=a[xv].cd=0;
        if(s[l]==g) a[xv].g=1;
        if(s[l]==c) a[xv].c=1;
        if(s[l]==d) a[xv].d=1;
        return;
    }
    a[xv].l=l,a[xv].r=r;
    int mid=(l+r)/2;
    build(xv*2,l,mid);
    build(xv*2+1,mid+1,r);
    pushup(xv);
}

tree query(int xv,int l,int r){
    int L=a[xv].l,R=a[xv].r,mid=(L+R)/2;
    if(l==L&&r==R){
        return a[xv];
    }
    if(r<=mid) return query(xv*2,l,r);
    else if(l>mid) return query(xv*2+1,l,r);
    else {
        tree ls=query(xv*2,l,mid),rs=query(xv*2+1,mid+1,r),xvv;
        xvv.g=ls.g+rs.g;
        xvv.c=ls.c+rs.c;
        xvv.d=ls.d+rs.d;
        xvv.cd=(ls.cd+rs.cd+ls.c*rs.d)%mod;
        xvv.gc=(ls.gc+rs.gc+ls.g*rs.c)%mod;
        xvv.gcd=(ls.gcd+rs.gcd+ls.gc*rs.d+ls.g*rs.cd)%mod;
        return xvv;
    }
}

int main()
{
    scanf("%s",s+1);
    len=strlen(s+1);
    build(1,1,len);
    scanf("%d",&q);
    while(q--){
        int l,r;scanf("%d%d",&l,&r);
        printf("%lld\n",query(1,l,r).gcd%mod);
    }
    return 0;
}

 

以上是关于区间 GCD的主要内容,如果未能解决你的问题,请参考以下文章

hdu 5869 区间gcd的求法及应用

hdu 5726 GCD

区间 GCD

hdu5381维护区间内所有子区间的gcd之和-线段树

树状数组区间更新区间查询以及gcd的logn性质

luoguP1890 gcd区间 [st表][gcd]