Codeforces 750E+2019南昌网络赛C(线段树维护自动机状态转移)

这个题解法之妙导致不想吐槽这场比赛了。。。

原题:Codeforces 750E New Year and Old Subsequence

复现:2019南昌网络赛C题 Hello 2019

原题题意:给定一个数字串,多次询问,每次询问使 [ l , r ] [l,r] [l,r]区间内存在"2017"这个序列,而不存在"2016"这个序列,至少需要删除多少个元素(无解输出 1 -1 1

复现题意:给定一个数字串,多次询问,每次询问使 [ l , r ] [l,r] [l,r]区间内存在"9102"这个序列,而不存在"8102"这个序列,至少需要删除多少个元素(无解输出 1 -1 1

思路参考cf题解


先讲原题

  1. 在计算理论中,确定有限状态自动机或确定有限自动机(英语:deterministic finite automaton, DFA)是一个能实现状态转移的自动机。对于一个给定的属于该自动机的状态和一个属于该自动机字母表 <mstyle displaystyle="true" scriptlevel="0"> Σ </mstyle> {\displaystyle \Sigma } Σ的字符,它都能根据事先给定的转移函数转移到下一个状态(这个状态可以是先前那个状态)。
  2. 而本题我们考虑如下 5 5 5种状态:
    状态 0 0 0:空状态 o r or or 初始状态
    状态 1 1 1:包含序列“ 2 2 2
    状态 2 2 2:包含序列“ 20 20 20
    状态 3 3 3:包含序列“ 201 201 201
    状态 4 4 4:包含序列“ 2017 2017 2017
  3. 如果我们将从 i i i状态转移到 j j j状态的过程看作一条花费为 c c c的边,则我们可以得到“ 2 2 2”,“ 0 0 0”,“ 1 1 1”,“ 7 7 7”,“ 6 6 6” 这 5 5 5个字符所对应的自动机,并且把这样 5 × 5 5×5 5×5的状态转移边放到一个矩阵中,就可以简易地表示自动机。考虑如下代码:
		if(s[l]=='2') t[now].v[0][1]=0, t[now].v[0][0]=1;
        if(s[l]=='0') t[now].v[1][2]=0, t[now].v[1][1]=1;
        if(s[l]=='1') t[now].v[2][3]=0, t[now].v[2][2]=1;
        if(s[l]=='7') t[now].v[3][4]=0, t[now].v[3][3]=1;
        if(s[l]=='6') t[now].v[3][3]=1, t[now].v[4][4]=1;
  1. 5 5 5个矩阵未被声明的元素都被定义为 i n f inf inf(主对角线为 0 0 0),表明当前自动机在接收这个字符时(这 5 5 5种字符),由 i i i状态转移到 j j j状态的最小花费为多少。拿 s [ i ] = 2 s[i]=2 s[i]=2来举例,若当前自动机需要接受字符’ 2 2 2’,则从 0 0 0状态(空状态)转移到 1 1 1状态(包含序列“ 2 2 2”),所需最小花费为 0 0 0;而从 0 0 0状态(空状态)转移到 0 0 0状态(空状态)的最小花费为 1 1 1,因为必须要删除当前所接受的字符才行;同时,其他除了主对角线上的转移都是不合法的,因此保留 i n f inf inf边,且主对角线上的所有转移(自身转移到自身)都是不需要有任何花费的,因为发生不了转移。后面 4 4 4种矩阵同理。但需要注意的是,在当前自动机接受字符’ 6 6 6‘时,从 3 3 3状态(包含序列“ 201 201 201”)到 3 3 3状态(包含序列“ 201 201 201”),必须删除这个’ 6 6 6'才行,因此花费为 1 1 1;从 4 4 4状态(包含序列“ 2017 2017 2017”)到 4 4 4状态(包含序列“2017”),也必须删除掉这个‘ 6 6 6’才行,因此花费也为 1 1 1,而其他的转移跟之前的一样啦。
  2. 而当我们想要合并两个自动机时,也就是合并两个串时,我们考虑像 f l o y d floyd floyd一样的思路,考虑由 i i i状态转移到 j j j状态中间经过 k k k状态(枚举 k k k状态)的最小花费
  3. 而如果用线段树维护自动机的合并的话,那是不是就非常好写并且清晰呢?废话不多说,这代码绝对好懂!

Codeforces 750E

#include "bits/stdc++.h"
#define hhh printf("hhh\n")
#define see(x) (cerr<<(#x)<<'='<<(x)<<endl)
using namespace std;
typedef long long ll;
typedef pair<int,int> pr;
inline int read() {int x=0;char c=getchar();while(c<'0'||c>'9')c=getchar();while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();return x;}

const int maxn = 2e5+10;
const int M = 5;
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
const double eps = 1e-7;

struct Mat{
    int v[M][M];
    void O() { memset(v,inf,sizeof(v)); }
    void I() { O(); for(int i=0; i<M; ++i) v[i][i]=0; }
    friend Mat operator * (Mat a, Mat b) {
        Mat c; c.O();
        for(int i=0; i<5; ++i) for(int j=0; j<5; ++j) for(int k=0; k<5; ++k)
            c.v[i][j]=min(c.v[i][j],a.v[i][k]+b.v[k][j]);
        return c;
    }
}t[maxn<<2];

int n, m, i, j;
char s[maxn];
void build(int l, int r, int now) {
    if(l==r) {
        t[now].I();
        if(s[l]=='2') t[now].v[0][1]=0, t[now].v[0][0]=1;
        if(s[l]=='0') t[now].v[1][2]=0, t[now].v[1][1]=1;
        if(s[l]=='1') t[now].v[2][3]=0, t[now].v[2][2]=1;
        if(s[l]=='7') t[now].v[3][4]=0, t[now].v[3][3]=1;
        if(s[l]=='6') t[now].v[3][3]=1, t[now].v[4][4]=1;
        return;
    }
    int m=(l+r)/2;
    build(l,m,now<<1), build(m+1,r,now<<1|1), t[now]=t[now<<1]*t[now<<1|1];
}

Mat query(int x, int y, int l, int r, int now) {
    if(x<=l&&r<=y) return t[now];
    int m=(l+r)/2;
    Mat res; res.I();
    if(x<=m) res=query(x,y,l,m,now<<1);
    if(y>m) res=res*query(x,y,m+1,r,now<<1|1);
    return res;
}

int main() {
    //ios::sync_with_stdio(false); cin.tie(0);
    n=read(), m=read(); scanf("%s", s+1);
    build(1,n,1);
    while(m--) {
        i=read(), j=read();
        printf("%d\n", (i=query(i,j,1,n,1).v[0][4])==inf?-1:i);
    }
}

2019南昌网络赛C

#include "bits/stdc++.h"
#define hhh printf("hhh\n")
#define see(x) (cerr<<(#x)<<'='<<(x)<<endl)
using namespace std;
typedef long long ll;
typedef pair<int,int> pr;
inline int read() {int x=0;char c=getchar();while(c<'0'||c>'9')c=getchar();while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();return x;}

const int maxn = 2e5+10;
const int M = 5;
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
const double eps = 1e-7;

struct Mat{
    int v[M][M];
    void O() { memset(v,inf,sizeof(v)); }
    void I() { O(); for(int i=0; i<M; ++i) v[i][i]=0; }
    friend Mat operator * (Mat a, Mat b) {
        Mat c; c.O();
        for(int i=0; i<5; ++i) for(int j=0; j<5; ++j) for(int k=0; k<5; ++k)
            c.v[i][j]=min(c.v[i][j],b.v[i][k]+a.v[k][j]); //这里a到b的转移改成b到a的转移,就相当于把原串翻转了
        return c;
    }
}t[maxn<<2];

int n, m, i, j;
char s[maxn];
void build(int l, int r, int now) {
    if(l==r) {
        t[now].I();
        if(s[l]=='2') t[now].v[0][1]=0, t[now].v[0][0]=1;
        if(s[l]=='0') t[now].v[1][2]=0, t[now].v[1][1]=1;
        if(s[l]=='1') t[now].v[2][3]=0, t[now].v[2][2]=1;
        if(s[l]=='9') t[now].v[3][4]=0, t[now].v[3][3]=1; //这里数字小改一下
        if(s[l]=='8') t[now].v[3][3]=1, t[now].v[4][4]=1;
        return;
    }
    int m=(l+r)/2;
    build(l,m,now<<1), build(m+1,r,now<<1|1), t[now]=t[now<<1]*t[now<<1|1];
}

Mat query(int x, int y, int l, int r, int now) {
    if(x<=l&&r<=y) return t[now];
    int m=(l+r)/2;
    Mat res; res.I();
    if(x<=m) res=query(x,y,l,m,now<<1);
    if(y>m) res=res*query(x,y,m+1,r,now<<1|1);
    return res;
}

int main() {
    //ios::sync_with_stdio(false); cin.tie(0);
    n=read(), m=read(); scanf("%s", s+1);
    build(1,n,1);
    while(m--) {
        i=read(), j=read();
        printf("%d\n", (i=query(i,j,1,n,1).v[0][4])==inf?-1:i);
    }
}
全部评论

相关推荐

头像
10-13 18:10
已编辑
东南大学 C++
。收拾收拾心情下一家吧————————————————10.12更新上面不知道怎么的,每次在手机上编辑都会只有最后一行才会显示。原本不想写凉经的,太伤感情了,但过了一天想了想,凉经的拿起来好好整理,就像象棋一样,你进步最快的时候不是你赢棋的时候,而是在输棋的时候。那废话不多说,就做个复盘吧。一面:1,经典自我介绍2,项目盘问,没啥好说的,感觉问的不是很多3,八股问的比较奇怪,他会深挖性地问一些,比如,我知道MMU,那你知不知道QMMU(记得是这个,总之就是MMU前面加一个字母)4,知不知道slab内存分配器-&gt;这个我清楚5,知不知道排序算法,排序算法一般怎么用6,写一道力扣的,最长回文子串反问:1,工作内容2,工作强度3,关于友商的问题-&gt;后面这个问题问HR去了,和中兴有关,数通这个行业和友商相关的不要提,这个行业和别的行业不同,别的行业干同一行的都是竞争关系,数通这个行业的不同企业的关系比较微妙。特别细节的问题我确实不知道,但一面没挂我。接下来是我被挂的二面,先说说我挂在哪里,技术性问题我应该没啥问题,主要是一些解决问题思路上的回答,一方面是这方面我准备的不多,另一方面是这个面试写的是“专业面试二面”,但是感觉问的问题都是一些主管面/综合面才会问的问题,就是不问技术问方法论。我以前形成的思维定式就是专业面会就是会,不会就直说不会,但事实上如果问到方法论性质的问题的话得扯一下皮,不能按照上面这个模式。刚到位置上就看到面试官叹了一口气,有一些不详的预感。我是下午1点45左右面的。1,经典自我介绍2,你是怎么完成这个项目的,分成几个步骤。我大致说了一下。你有没有觉得你的步骤里面缺了一些什么,(这里已经在引导我往他想的那个方向走了),比如你一个人的能力永远是不够的,,,我们平时会有一些组内的会议来沟通我们的所思所想。。。。3,你在项目中遇到的最困难的地方在什么方面4,说一下你知道的TCP/IP协议网络模型中的网络层有关的协议......5,接着4问,你觉得现在的socket有什么样的缺点,有什么样的优化方向?6,中间手撕了一道很简单的快慢指针的问题。大概是在链表的倒数第N个位置插入一个节点。————————————————————————————————————10.13晚更新补充一下一面说的一些奇怪的概念:1,提到了RPC2,提到了fu(第四声)拷贝,我当时说我只知道零拷贝,知道mmap,然后他说mmap是其中的一种方式,然后他问我知不知道DPDK,我说不知道,他说这个是一个高性能的拷贝方式3,MMU这个前面加了一个什么字母我这里没记,别问我了4,后面还提到了LTU,VFIO,孩子真的不会。
走呀走:华子二面可能会有场景题的,是有些开放性的问题了
点赞 评论 收藏
分享
10-13 13:49
南京大学 财务
饿魔:笑死我了,你简直是个天才
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务