多校连训Day10 20210428

这篇博客介绍了S星球生物的氨基酸序列构成,特别关注于冠状病毒的氨基酸序列研究。文章讨论了一种算法,该算法通过分块处理优化了对氨基酸序列的维护和查询操作,以应对不同致死掩码的查询,同时处理了病毒数量的增减。内容涵盖了算法设计、时间复杂度分析以及实际实现,展示了在限制时间内解决复杂问题的方法。

T1 氨基酸序列 amino

题目描述

在S星球,生物体需要的氨基酸只有两种 : 得氨酸和便氨酸。

因此,S星球的蛋白质仅由这两种氨基酸构成肽链构成。我们用0和1分别表示得氨酸和便氨酸,那么生物体的蛋白质可以表示成一段由0和1构成的氨基酸序列。

Θ国珂学院在研究完该冠状病毒的RNA序列后,转而开始研究更简单的氨基酸序列。

研究表明,该冠状病毒的氨基酸序列的长度为L。也就是说,如果以氨基酸序列为标准,则一共有 2 L 2^L 2L种不同的冠状病毒。

8224年4月1日,作为 S 星球疫情的一个转折点,她们已经对 SARS-CoV-233 病毒的氨基酸序列有了初步的了解,并打算在培养皿中进一步观察。

具体地,将在接下来的q天,依次发生如下事件,其中每天发生下面两种事件之一:

  1. 培养皿中,氨基酸序列为 s e q seq seq的病毒的数量增加了 v a l val val,其中 v a l > 0 val>0 val>0表示增加了 v a l val val个个体, v a l < 0 val<0 val<0表示减少了 v a l val val个个体, v a l = 0 val=0 val=0表示没有发生变化。
  2. 研究表明,给定一个只包含 0 / 1 / ∗ 0/1/* 0/1/的序列 m a s k mask mask(称为致死掩码)如果氨基酸序列 s e q seq seq满足,对于 m a s k mask mask中每个非 ∗ * 的位置, s e q seq seq中对应的氨基酸和 m a s k mask mask相同,则称该氨基酸序列是致死的。珂学家想知道现在的培养皿中有多少个病毒的氨基酸序列是致死的。

由于生物的进化,不同时间下致死掩码 m a s k mask mask是不相同的,你需要对这些不同的 m a s k mask mask分别作出回答。

输入格式

第一行包含两个正整数 L , q L,q L,q,分别表示氨基酸序列的长度和事件的个数。

接下来 q q q行,每行描述一个事件,格式如下:

  1. I seq val 表示氨基酸序列为 s e q seq seq的病毒数量增加了 v a l val val,其中 s e q seq seq为长度为 L L L 0 / 1 0/1 0/1串。
  2. Q mask 表示对于致死掩码 m a s k mask mask,询问培养皿中有多少个病毒的氨基酸序列是致死的,其中 m a s k mask mask为长度为的 0 / 1 / ∗ 0/1/* 0/1/串。

输出格式

对于每次 Q Q Q事件,输出一行一个整数,表示氨基酸序列是致死的病毒数量模
2 32 2^{32} 232的结果。

样例1

4 8
Q ****
I 0101 5
I 1110 3
Q ****
Q *0**
Q ***0
I 0101 -3
Q *1**
0
8
0
3
5

数据范围

1 ≤ L ≤ 18 ; 1 ≤ q ≤ 5 ∗ 1 0 5 ; − 1 0 9 ≤ v a l ≤ 1 0 9 ; ∣ s e q ∣ = ∣ m a s k ∣ = L 1\leq L \leq 18 ; 1\leq q \leq 5*10^5 ; -10^9\leq val \leq 10^9 ; |seq|=|mask|=L 1L18;1q5105;109val109;seq=mask=L

算法一 3pts

这个部分分根本没有询问操作。随便写一个没输出的程序就行。

但是这个部分分在之后的测试点有可能会TLE掉 不要问我怎么知道的

写完之后最好判一手吧,反正也费不了多少事。

算法二 3+6+12+6+7pts

暴力维护,写一个Trie树或这枚举子集就好了。

算法三 58pts

对于只有mask中不含1的部分,相当于动态维护子集和,可以在 O ( q ∗ 2 L 2 ) O(q*2^{\frac{L}{2}}) O(q22L)的时间内完成,具体做法如下:

具体的,我们将 S S S分块,分成大小为 L 2 \frac{L}{2} 2L的两部分 S 1 , S 2 S_1,S_2 S1,S2。然后维护子集和时只对 S 2 S_2 S2部分求子集和。

这样修改的时候只需要对 S 2 S_2 S2部分进行枚举,查询时对 S 1 S_1 S1求和即可,时间复杂度均为 O ( 2 L 2 ) O(2^\frac{L}{2}) O(22L)

(如果对这里有疑问可以直接看算法八和代码)

接下来的算法四五都是STD的做法,算法六是STD优化,算法七是STD自称尚未实现的做法,而算法八是我自己写(shui)的做法,如果不想看题解做法可以看看。

算法四

在这里插入图片描述
在这里插入图片描述

链接:毒蛇越狱

算法五

在这里插入图片描述

算法六 (std 优化)

在这里插入图片描述

算法七 (尚未实现)

在这里插入图片描述

算法八

让我们回顾算法三:

具体的,我们将 S S S分块,分成大小为 L 2 \frac{L}{2} 2L的两部分 S 1 , S 2 S_1,S_2 S1,S2。然后维护子集和时只对 S 2 S_2 S2部分求子集和。

因为 1 ≤ n ≤ 18 1\leq n\leq 18 1n18,所以在 n = 18 n=18 n=18时两块的长度都为9。

考虑这个算法的瓶颈在哪里:

  1. 运行时间方面:每次都是 O ( q ∗ 2 9 ) O(q*2^9) O(q29),时间极为极限,在这个辣鸡评测机上几乎不可能过不去。
  2. 内存方面:因为子集处理时相当于把询问的串选定某些 0 , 1 0,1 0,1位置换成 ? ? ?,一共有三种状态, 3 9 3^9 39因为有三没法位运算所以会非常慢,而如果开到 4 9 4^9 49 2 9 ∗ 4 9 2^9*4^9 2949unsigned int数组正正好好512M,稍微开一点就会炸,所以间接地导致时间慢。

再想想算法三实际上的时间复杂度:

  1. 修改操作:因为要枚举子集,就是满满的 2 9 2^9 29没法优化,而且三次方运算还拖慢时间。
  2. 查询操作:与前面9位的 ? ? ?个数s有关,时间复杂度是 2 s 2^s 2s

于是我就想着优化掉修改操作的常数。

  • 因为 2 9 2^9 29太极限了,考虑将 S S S分块大小设成 S 1 = 10 , S 2 = 8 S_1=10,S_2=8 S1=10,S2=8

  • 这样的话查询操作就是 2 8 2^8 28了。

然后回想之前的话:

3 9 3^9 39因为有三没法位运算所以会非常慢,而如果开到 4 9 4^9 49 2 9 ∗ 4 9 2^9*4^9 2949unsigned int数组正正好好512M

  • 2 9 2^9 29换成 2 8 2^8 28后,如果四为压位基底,大小是 2 10 ∗ 4 8 2^{10}*4^8 21048,256M!然后就可以将这堆压位操作换成更快的位运算。

但是这样有什么麻烦呢?很显然,查询的最坏复杂度变成了 2 10 2^{10} 210

考虑什么时候会达到这个复杂度:

  1. 当询问串前10位全是?,一次复杂度 2 10 2^{10} 210
  2. 询问串前10位只有一个数字,一次复杂度 2 9 2^{9} 29

我们发现,这几种情况是有限的:

  • 前十位全问号只有一种情况
  • 前十位有九个问号只有20种情况

所以,可以对这21种情况统计末尾,记下每种末尾串的数量,暴力枚举子集统计。

for(i=0;i<10;i++)
    f1[i][((x>>8)>>i)&1][x&255]+=y;
c[x&255]+=y;
tmp2=(xx&255);
for(i=tmp2;i;i=tmp2&(i-1))
    ans+=c[(xs&255)|i];
ans+=c[xs&255];

最后贴上我的代码:

#include <bits/stdc++.h>
using namespace std;
int n,q;
unsigned int s[1<<10][1<<16];
// unsigned int f2[10][10][2][2][1<<8];
unsigned int f1[10][2][1<<8],c[1<<8];
inline int t10(register int x){return x?1:0;}
void add(register int x,register unsigned int y){
    register int i,j;
    for(i=0;i<(1<<8);i++)
        s[x>>8][(i<<8)|(x&(i^255))]+=y;
    for(i=0;i<10;i++)
        f1[i][((x>>8)>>i)&1][x&255]+=y;
    c[x&255]+=y;
}
inline int popc(int x){
    int ret=0;
    for(;x;x-=x&-x)
        ret++;
    return ret;
}
unsigned int query(register int xx,register int xs){
    register int tmp1,tmp2,tmp3,tmp4,i,j;
    register unsigned int ans=0;
    tmp1=popc(xx>>8);
    tmp2=(xx>>8);
    if(tmp1<=8){
        for(i=tmp2;i;i=tmp2&(i-1))
            ans+=s[(xs>>8)|i][((xx&255)<<8)|(xs&255)];
        ans+=s[(xs>>8)][((xx&255)<<8)|(xs&255)];
    } else if(tmp1==10){
        tmp2=(xx&255);
        for(i=tmp2;i;i=tmp2&(i-1))
            ans+=c[(xs&255)|i];
        ans+=c[xs&255];
    } else {
        for(i=0;i<10;i++)
            if(!(xx&(1<<(i+8)))){
                tmp3=i,tmp4=(xs>>(i+8))&1;
                break;
            }
        tmp2=(xx&255);
        for(i=tmp2;i;i=tmp2&(i-1))
            ans+=f1[tmp3][tmp4][(xs&255)|i];
        ans+=f1[tmp3][tmp4][(xs&255)];
    }
    return ans;
}
inline int read(){
    register int x=0,f=1;
    register char c;
    while(!isdigit(c=getchar()))
        if(c=='-') f=-1;
    do x=(x<<3)+(x<<1)+(c^48);
    while(isdigit(c=getchar()));
    return x*f;
}
struct query{
    bool opt;
    int a,b;
}Q[500010];
int main(){
    #ifdef ONLINE_JUDGE
        freopen("amino.in","r",stdin);
        freopen("amino.out","w",stdout);
    #endif
    scanf("%d%d",&n,&q);
    char ch;
    unsigned int utmp;
    int xx,xs;
    bool flag=true;
    for(int i=1,x,y;i<=q;i++){
        do ch=getchar();
        while(ch!='Q'&&ch!='I');
        if(ch=='I') {
            Q[i].opt=false;
            x=0;
            ch=getchar();
            for(int j=1;j<=n;j++){
                ch=getchar();
                x=(x<<1)+(ch=='1');
            }
            Q[i].a=x,Q[i].b=read();
            // add(x,read());
        } else {
            Q[i].opt=true;
            flag=false;
            xx=xs=0;
            ch=getchar();
            for(int j=1;j<=n;j++){
                ch=getchar();
                xx<<=1,xs<<=1;
                if(ch=='*') xx|=1;
                else if(ch=='1') xs|=1;
            }
            Q[i].a=xx,Q[i].b=xs;
            // printf("%u\n",query());
        }
    }
    if(flag) return 0;
    for(int i=1;i<=q;i++)
        if(!Q[i].opt) add(Q[i].a,Q[i].b);
        else printf("%u\n",query(Q[i].a,Q[i].b));
    return 0;
}

T2

T3

在这里插入图片描述

标题基于Python的汽车之家网站舆情分析系统研究AI更换标题第1章引言阐述汽车之家网站舆情分析的研究背景、意义、国内外研究现状、论文方法及创新点。1.1研究背景与意义说明汽车之家网站舆情分析对汽车行业及消费者的重要性。1.2国内外研究现状概述国内外在汽车舆情分析领域的研究进展与成果。1.3论文方法及创新点介绍本文采用的研究方法及相较于前人的创新之处。第2章相关理论总结和评述舆情分析、Python编程及网络爬虫相关理论。2.1舆情分析理论阐述舆情分析的基本概念、流程及关键技术。2.2Python编程基础介绍Python语言特点及其在数据分析中的应用。2.3网络爬虫技术说明网络爬虫的原理及在舆情数据收集中的应用。第3章系统设计详细描述基于Python的汽车之家网站舆情分析系统的设计方案。3.1系统架构设计给出系统的整体架构,包括数据收集、处理、分析及展示模块。3.2数据收集模块设计介绍如何利用网络爬虫技术收集汽车之家网站的舆情数据。3.3数据处理与分析模块设计阐述数据处理流程及舆情分析算法的选择与实现。第4章系统实现与测试介绍系统的实现过程及测试方法,确保系统稳定可靠。4.1系统实现环境列出系统实现所需的软件、硬件环境及开发工具。4.2系统实现过程详细描述系统各模块的实现步骤及代码实现细节。4.3系统测试方法介绍系统测试的方法、测试用例及测试结果分析。第5章研究结果与分析呈现系统运行结果,分析舆情数据,提出见解。5.1舆情数据可视化展示通过图表等形式展示舆情数据的分布、趋势等特征。5.2舆情分析结果解读对舆情分析结果进行解读,提出对汽车行业的见解。5.3对比方法分析将本系统与其他舆情分析系统进行对比,分析优劣。第6章结论与展望总结研究成果,提出未来研究方向。6.1研究结论概括本文的主要研究成果及对汽车之家网站舆情分析的贡献。6.2展望指出系统存在的不足及未来改进方向,展望舆情
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值