【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\n", 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\n", ans);
    return 0;
}
posted @ 2017-01-16 09:35  abclzr  阅读(601)  评论(2编辑  收藏  举报