核苷酸

题目描述

DNA中包含四种核苷酸,上面具有不同的含氮碱基,分别是:腺嘌呤(A)、胸腺嘧啶(T)、鸟嘌呤(G)、胞嘧啶(C)。我们可以用形如ATTGCA…的字符串来代表一条脱氧核苷酸序列。
我们需要追踪一段高变异生物的核苷酸序列。在这种生物中,其DNA序列很容易发生替换,即将某一位的核苷酸替换为其他核苷酸。
我们有一段感染串,这一条DNA单链极有可能替换原来DNA链中的核苷酸。已知这条单链会作用于 [ l , r ]中的位置。这条单链会不重叠复制成一条长链,然后顺序感染 [ l , r ]中的核苷酸。如果两个位置间的核苷酸不同,那么DNA链上的核苷酸就会被替换。
现在研究人员想知道,对于感染串 T ,在 [ l , r ] 上有多少个位置不会受到感染。

输入

第一行一串字符串,初始DNA链 S 。 S 仅由A,T,G,C四个大写字母组成。
接下来一行带有一个整数 Q 。接下来 Q 行每行一个询问,格式如下:
1 x C,表示第 x 位置上的核苷酸被替换成字符 C
2 l r T,表示有可能有一个感染串 T ,在 [ l , r ] 间作用。
注意,每次感染串并不会对原串做出任何修改。

输出

对于每个查询 2 ,输出不会受感染的核苷酸位置数量。

题解

先orzxjq神犇。显然我们拿着 l,r l , r 和给定串去比较是非常难处理的,给人只能暴力的感觉,于是我们把语言规范化一点,len是T的长度, S[x]==T[y] | xl+1y (mod len) S [ x ] == T [ y ]   |   x − l + 1 ≡ y   ( m o d   l e n ) 时才能对答案有贡献,因为处理的是S,所以改写成 S[x]==T[y] | xy+l1 (mod len) S [ x ] == T [ y ]   |   x ≡ y + l − 1   ( m o d   l e n ) ,这样贡献的条件变成了3个,字符,取模数,余数,结合一早就发现的T长度很小这点,我们可以处理出一个树状数组 f[p][m][r][x] f [ p ] [ m ] [ r ] [ x ] 依次表示字符,取模数,余数,S串中前x个的答案。修改就是单点修改,妥妥的树状数组啊。每次修改的时候遍历取模数即可。
这题的关键是把给定条件转化为标准的式子,再加上观察原题中的T长度很小这一点,就可以找到正解了。

代码

#include <bits/stdc++.h>
#define maxn 100005
#define INF 0x3f3f3f3f
#define eps 1e-7
typedef long long LL;
using namespace std;
struct EDGE{
   int u,v,w,nxt;
}e[maxn];
int n,m;
char s[maxn];
int mp[200],arr[4][11][10][maxn];
int query(int p,int m,int r,int x){int ans=0;for(;x;x-=x&-x)ans+=arr[p][m][r][x];return ans;}
void update(int p,int m,int r,int x,int w){for(;x<=n;x+=x&-x)arr[p][m][r][x]+=w;}
void work1(){
    char a; int x;
    scanf("%d %c",&x,&a);
    for(int i=1;i<=10;i++) update(mp[s[x]],i,x%i,x,-1);
    for(int i=1;i<=10;i++) update(mp[a],i,x%i,x,1);
    s[x]=a;
}
void work2(){
    char a[100]; int l,r,ans=0;
    scanf("%d%d %s",&l,&r,a+1);
    int len=strlen(a+1);
    for(int i=1;i<=len;i++){
        ans+=query(mp[a[i]],len,(i+l-1)%len,r)-query(mp[a[i]],len,(i+l-1)%len,l-1);
    }
    printf("%d\n",ans);
}
int main(){
    int q;
    //freopen("evolution.in","r",stdin);
    //freopen("evolution.out","w",stdout);
    mp['A']=0; mp['T']=1; mp['G']=2; mp['C']=3;
    scanf("%s%d",s+1,&q);
    n=strlen(s+1);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=10;j++){
            update(mp[s[i]],j,i%j,i,1);
        }
    }
    for(int i=1;i<=q;i++){
        int type;
        scanf("%d",&type);
        if(type==1) work1();
        else work2();
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jarden_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值