区间 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;
}
时间: 2024-10-22 11:23:50

区间 GCD的相关文章

dutacm.club 1094: 等差区间(RMQ区间最大、最小值,区间GCD)

1094: 等差区间 Time Limit:5000/3000 MS (Java/Others)   Memory Limit:163840/131072 KB (Java/Others)Total Submissions:655   Accepted:54 [Submit][Status][Discuss] Description 已知一个长度为 n 的数组 a[1],a[2],-,a[n],我们进行 q 次询问,每次询问区间 a[l],a[l+1],-,a[r?1],a[r] ,数字从小到大

HDU 5726 GCD 区间GCD=k的个数

GCD Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 2742    Accepted Submission(s): 980 Problem Description Give you a sequence of N(N≤100,000) integers : a1,...,an(0<ai≤1000,000,000). There ar

bzoj 5028: 小Z的加油店——带修改的区间gcd

Description 小Z经营一家加油店.小Z加油的方式非常奇怪.他有一排瓶子,每个瓶子有一个容量vi.每次别人来加油,他会让 别人选连续一段的瓶子.他可以用这些瓶子装汽油,但他只有三种操作: 1.把一个瓶子完全加满: 2.把一个瓶子完全倒空: 3.把一个瓶子里的汽油倒进另一个瓶子,直到倒出瓶子空了或者倒进的瓶子满了. 当然,为了回馈用户,小Z会时不时选择连续一段瓶子,给每个瓶子容积都增加x. 为了尽可能给更多的人加油,每次客户来加油他都想知道他能够倒腾出的汽油量最少是多少? 当然他不会一点汽

Codeforces 914D - Bash and a Tough Math Puzzle 线段树,区间GCD

题意: 两个操作, 单点修改 询问一段区间是否能在至多一次修改后,使得区间$GCD$等于$X$ 题解: 正确思路; 线段树维护区间$GCD$,查询$GCD$的时候记录一共访问了多少个$GCD$不被X整除的区间即可,大于一个就NO 要注意的是,如果真的数完一整个区间,肯定会超时,因此用一个外部变量存储数量,一旦超过一个,就停止整个查询 #include <bits/stdc++.h> #define endl '\n' #define ll long long #define IO ios::s

FZU2224 An exciting GCD problem 区间gcd预处理+树状数组

分析:(别人写的) 对于所有(l, r)区间,固定右区间,所有(li, r)一共最多只会有log个不同的gcd值, 可以nlogn预处理出所有不同的gcd区间,这样区间是nlogn个,然后对于询问离线处理, 用类似询问区间不同数字的方法,记录每个不同gcd最后出现的位置,然后用树状数组进行维护 注:我是看了这段代码会的,但是他的nlogn预处理我不会,我会nlog^2n的 dp[i][j]代表以i为右端点,向左延伸2^j个点(包括i)的gcd,然后因为这样的gcd满足递减,所以可以二分找区间 代

hdu 5869 区间gcd的求法及应用

题意:长度n的序列, m个询问区间[L, R], 问区间内的所有连续子段的不同GCD值有多少种. 题解: 1.因为n个数的gcd等于前n-1个数的gcd值再于第n个数gcd一下的值,再加上如果固定终点,区间向前延伸越多gcd必定是非严格递减的,所以我们可以预处理出以每一个数为终点的所有的后缀的gcd,每次求出第i个数为终点后记录下所有的gcd值再与第i+1个数求gcd,这个很好做. 2.将所有的查询按右区间从小到大排序,同时将第一步记录的值在树状数组中更新,然后用树状数组区间求和就好了,这一步在

Luogu T9376 区间GCD

题目背景 无 题目描述 给定一长度为n的动态序列,请编写一种数据结构,要求支持m次操作,包括查询序列中一闭区间中所有数的GCD,与对一闭区间中所有数加上或减去一个值. 输入输出格式 输入格式: 第1行两个数n,m,表示序列长度和操作次数. 第2行n个数ai,表示给定序列. 第3行至第m+2行,每行3~4个数: (1) 1 x y k 表示将[x,y]上的所有数加上k. (2) 2 x y 表示询问[x,y]上所有数的GCD. 输出格式: 对所有操作2,输出一个数,表示询问结果. 输入输出样例 输

区间GCD

输入格式: 输出格式: 样例输入: glygshcyjcdzy 3 1 11 2 11 2 10 样例输出: 4 2 0 数据范围: 时间限制: 3S 空间限制: 512MB 这题是一个统计题.如果只是两个字母,那就很好做了,然而,三个字母的话,很难直接统计.如果枚举g,还药确定c,d的相对位置,枚举c和d也同样.更何况有Q次询问. 想到这题又是有多次询问,也没有修改操作,而且能在知道[L,R](或[L+1,R+1])的情况下知道[L+1,R]和[L,R+1],所以,我们确定,这题非常适合分块+

E. Kamil and Making a Stream 区间gcd

E. Kamil and Making a Stream 这个题目要用到一个结论,就是区间一个区间长度为n的不同的gcd不会超过logn 个, 其实就是知道这个题目可以暴力就好了. 然后就是对于每一个节点,我都存从祖先到这个节点的所有的gcd,用一个vector存下来. 然后因为这个vector的size 不会很大,所以就可以直接暴力往下转移. #include <cstdio> #include <cstring> #include <cstdlib> #includ