区间 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 取模后输出。样例输入glygshcyjcdzy31 112 112 10样例输出420数据规模与约定对于 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#include #include #include #include #include #define ll long long#define MAXN 80100using 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;}