zoukankan      html  css  js  c++  java
  • BZOJ4516 [Sdoi2016]生成魔咒 【后缀自动机】

    题目

    魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示。例如可以将魔咒字符 1、2 拼凑起来形成一个魔咒串 [1,2]。
    一个魔咒串 S 的非空字串被称为魔咒串 S 的生成魔咒。
    例如 S=[1,2,1] 时,它的生成魔咒有 [1]、[2]、[1,2]、[2,1]、[1,2,1] 五种。S=[1,1,1] 时,它的生成魔咒有 [1]、
    [1,1]、[1,1,1] 三种。最初 S 为空串。共进行 n 次操作,每次操作是在 S 的结尾加入一个魔咒字符。每次操作后都
    需要求出,当前的魔咒串 S 共有多少种生成魔咒。

    输入格式

    第一行一个整数 n。
    第二行 n 个数,第 i 个数表示第 i 次操作加入的魔咒字符。
    1≤n≤100000。,用来表示魔咒字符的数字 x 满足 1≤x≤10^9

    输出格式

    输出 n 行,每行一个数。第 i 行的数表示第 i 次操作后 S 的生成魔咒数量

    输入样例

    7

    1 2 3 3 3 1 2

    输出样例

    1

    3

    6

    9

    12

    17

    22

    题解

    用到后缀自动机的一个性质:

    节点u所代表的子串数 = step[u] - step[pre[u]]

    原因:
    显然u所代表的子串数为[min(u),max(u)],其中min,max均代表从根到u的路径的长度最值
    而且有这样一个性质:u父亲的max(fa) = min(u) - 1
    所以u代表的子串数可以写成如上所示

    我们只需要在每次建立父亲指针和断开父亲指针时维护答案就好了

    #include<iostream>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<map>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
    #define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
    using namespace std;
    const int maxn = 200005,maxm = 100005,INF = 1000000000;
    inline int read(){
    	int out = 0,flag = 1; char c = getchar();
    	while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
    	while (c >= 48 && c <= 57) {out = (out << 3) + (out << 1) + c - '0'; c = getchar();}
    	return out * flag;
    }
    map<int,int> ch[maxn];
    int pre[maxn],step[maxn],cnt,last,n;
    LL ans;
    int upd(int u){return step[u] - step[pre[u]];}
    void ins(int x){
    	int p = last,np = ++cnt;
    	last = np; step[np] = step[p] + 1;
    	while (p && !ch[p].count(x)) ch[p][x] = np,p = pre[p];
    	if (!p) pre[np] = 1,ans += upd(np);
    	else {
    		int q = ch[p][x];
    		if (step[q] == step[p] + 1) pre[np] = q,ans += upd(np);
    		else {
    			int nq = ++cnt; step[nq] = step[p] + 1;
    			ch[nq] = ch[q]; pre[nq] = pre[q]; ans += upd(nq) - upd(q);
    			pre[np] = pre[q] = nq; ans += upd(np) + upd(q);
    			while (p && ch[p][x] == q) ch[p][x] = nq,p = pre[p];
    		}
    	}
    }
    int main(){
    	n = read(); int x; cnt = last = 1;
    	REP(i,n) x = read(),ins(x),printf("%lld
    ",ans);
    	return 0;
    }
    
    
  • 相关阅读:
    HTTP协议--详解
    汇编语言前五章总结
    [转] vscode C/C++ 插件预定义环境变量(linux)
    C++小细节
    CC++ 如何确定一个变量的类型(恶心的指针)
    Ubuntu下安装tensorflow
    github 上不去
    应用层01-HTTP
    C++ 传递数组引用
    C++ 命令行窗口打印二叉树(图形)
  • 原文地址:https://www.cnblogs.com/Mychael/p/8308454.html
Copyright © 2011-2022 走看看