• 【BZOJ 3926】【ZJOI 2015】诸神眷顾的幻想乡


    http://www.lydsy.com/JudgeOnline/problem.php?id=3926
    广义后缀自动机的例题,感觉广义后缀自动机好恶心。。。
    广义后缀自动机是对一个trie建立的后缀自动机,能识别trie上的所有子串。right集合代表的是trie树上的节点集合。
    具体做法是把last移到“在trie树上要添加节点的节点”在后缀自动机的状态节点上,然后套插入模板。
    插入模板的运行过程跟对一个串建立后缀自动机有些不同。
    假设插入w,首先np节点的(|Right|)可以不再等于1,而且如果last存在w的转移函数,特判一下转移到的点的maxlen是否为last的maxlen+1。如果是,直接把last移到这个点就行了。
    不移动也可以,这样会新建一个np节点,np->par为last->go,相当于把np和last->go看成一个点。
    如果转移到的点的maxlen大于last的maxlen,那么在插入模板里需要新建一个nq节点,nq的maxlen为last的maxlen+1。
    同时插入模板里还要把np和q的par指针指向nq。注意这里np和nq的right集合是完全相同的!但是np->par=nq,不符合后缀自动机里“真包含”的定义,不过我们还是可以把这里的np和nq看成一个点。
    其他情况跟一般的后缀自动机构造一样。
    最后把last移向np,如果np和nq的right集合完全相同,就把它们一起看成一个“大点”,last在np上没有某个转移函数就跳到nq上看有没有这个转移函数,相当于这个“大点”的转移函数是np和nq的转移函数的并集。
    时空复杂度还是(O(n))
    在mrazer神犇的帮助下终于理解了qwq

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    
    ll ans = 0;
    struct State {
    	State *par, *go[10];
    	int val;
    	State(int _num) : val(_num) {par = 0; memset(go, 0, sizeof(go));}
    } *root, *last;
    
    void extend(int w) {
    	if (last->go[w] && last->go[w]->val == last->val + 1) {
    		last = last->go[w];
    		return;
    	}
    	State *p = last;
    	State *np = new State(p->val + 1);
    	while (p && p->go[w] == 0)
    		p->go[w] = np, p = p->par;
    	if (p == 0) np->par = root;
    	else {
    		State *q = p->go[w];
    		if (p->val + 1 == q->val) np->par = q;
    		else {
    			State *nq = new State(p->val + 1);
    			memcpy(nq->go, q->go, sizeof(q->go));
    			nq->par = q->par;
    			q->par = np->par = nq;
    			while (p && p->go[w] == q)
    				p->go[w] = nq, p = p->par;
    		}
    	}
    	last = np; ans += np->val - np->par->val;
    }
    
    const int N = 100003;
    int n, c, cnt = 0, point[N], du[N], col[N];
    struct node {int nxt, to;} E[N << 1];
    void ins(int u, int v) {E[++cnt] = (node) {point[u], v}; point[u] = cnt;}
    
    void dfs(int x, int fa) {
    	extend(col[x]);
    	State *now = last;
    	for (int i = point[x], v = E[i].to; i; v = E[i = E[i].nxt].to)
    		if (v != fa) {
    			dfs(v, x);
    			last = now;
    		}
    }
    
    int main() {
    	root = last = new State(0);
    	scanf("%d%d", &n, &c);
    	for (int i = 1; i <= n; ++i) scanf("%d", col + i);
    	int u, v;
    	for (int i = 1; i < n; ++i) {
    		scanf("%d%d", &u, &v);
    		ins(u, v); ins(v, u);
    		++du[u]; ++du[v];
    	}
    	
    	for (int i = 1; i <= n; ++i)
    		if (du[i] == 1)
    			last = root, dfs(i, -1);
    	printf("%lld
    ", ans);
    	return 0;
    }
    

    下面的是讨论版的没有任何多余的节点的广义后缀自动机(及不需要把两个点理解成一个大点),感觉自己好闲啊qwq

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    
    ll ans = 0;
    struct State {
        State *par, *go[10];
        int val;
        State(int _num) : val(_num) {par = 0; memset(go, 0, sizeof(go));}
    } *root, *last;
    
    void extend(int w) {
        if (last->go[w] && last->go[w]->val == last->val + 1) {
            last = last->go[w];
            return;
        }
        State *p = last;
        State *np = new State(p->val + 1);
        while (p && p->go[w] == 0)
            p->go[w] = np, p = p->par;
        if (p == 0) np->par = root;
        else {
            State *q = p->go[w];
            if (p->val + 1 == q->val) np->par = q;
            else if (p != last) {
    	            State *nq = new State(p->val + 1);
    	            memcpy(nq->go, q->go, sizeof(q->go));
    	            nq->par = q->par;
    	            q->par = np->par = nq;
    	            while (p && p->go[w] == q)
    	                p->go[w] = nq, p = p->par;
            	} else {
            		memcpy(np->go, q->go, sizeof(q->go));
            		np->par = q->par;
            		q->par = np;
            		while (p && p->go[w] == q)
            			p->go[w] = np, p = p->par;
            		last = np; return;
            	}
        }
        last = np; ans += last->val - last->par->val;
    }
    
    const int N = 100003;
    int n, c, cnt = 0, point[N], du[N], col[N];
    struct node {int nxt, to;} E[N << 1];
    void ins(int u, int v) {E[++cnt] = (node) {point[u], v}; point[u] = cnt;}
    
    void dfs(int x, int fa) {
        extend(col[x]);
        State *now = last;
        for (int i = point[x], v = E[i].to; i; v = E[i = E[i].nxt].to)
            if (v != fa) {
                dfs(v, x);
                last = now;
            }
    }
    
    int main() {
        root = last = new State(0);
        scanf("%d%d", &n, &c);
        for (int i = 1; i <= n; ++i) scanf("%d", col + i);
        int u, v;
        for (int i = 1; i < n; ++i) {
            scanf("%d%d", &u, &v);
            ins(u, v); ins(v, u);
            ++du[u]; ++du[v];
        }
        
        for (int i = 1; i <= n; ++i)
            if (du[i] == 1)
                last = root, dfs(i, -1);
        printf("%lld
    ", ans);
        return 0;
    }
    
  • 相关阅读:
    Telink SDK 的编译设置(以825x系列的一个SDK为例)
    Ubuntu换源后代号不匹配引发的问题
    char和signed char不同编译器下的使用反思
    HudiFlink CDC将MySQL数据写入hudi
    Hudi集成Flink(Flink操作hudi表)
    HudiSparkSQL增删改查Hudi表
    HudiFlink SQL实时读取kafka数据写入Hudi表
    HudiStructuredStreaming流式写入Hudi
    HudiFlink SQL实时读取Hudi表数据
    Hudi通过Hive查询hudi表数据
  • 原文地址:https://www.cnblogs.com/abclzr/p/6288505.html
Copyright © 2020-2023  润新知