[BZOJ4545]DQS的Trie(广义SAM+离线+树状数组)

[BZOJ4545]DQS的Trie(广义SAM+离线+树状数组)

题面

一颗 Trie 树,q 次操作,操作有3种:
1.求这棵树上本质不同的子串数量
2.插入一个子树,保证总大小不超过 100000
3.询问一个字符串在 Trie 树上出现过多少次,保证所有询问串总长度不超过 100000

分析

不考虑插入,操作1就是问所有节点的(len(x)-len(link(x)))之和。操作3就是字符串在SAM上匹配到的节点,节点的right集合大小,即parent树子树大小(去掉复制出来的节点)之和。
SAM+LCT维护子树显然是可行的,但在考场上不一定能写出来。不妨把操作离线,把所有子树插入后的Trie树建出来,然后对它建广义后缀自动机。然后DFS整个parent树,记录DFS序,用来把子树和转化成区间和。容易发现虽然树的形态在变,但用最终树的DFS序来求和是等价的。于是可以用树状数组按照DFS序维护每个点的子树大小
接着遍历操作,发现操作2实际上就是最终自动机的一些节点由未出现变成出现的过程。首先按照DFS序单点加1,表示增加了一个出现的节点。然后沿着link往上跳,把节点由未出现标记成出现,跳到已经出现的节点位置。一边跳一边累加当前节点的(len(x)-len(link(x)))之和。由于每个点只会被标记一次,总复杂度是(O(n))的。
对于询问,1操作直接输出累加的和。3操作在SAM上匹配,然后找到匹配到的节点,DFS序区间查询子树大小。

该离线做法不仅不容易写错,且常数比LCT小。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define maxn 500000
#define maxc 26
using namespace std;
typedef long long ll;
template<typename T> void qread(T &x) {
	x=0;
	T sign=1;
	char c=getchar();
	while(c<'0'||c>'9') {
		if(c=='-') sign=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9') {
		x=x*10+c-'0';
		c=getchar();
	}
	x=x*sign;
}
template<typename T> void qprint(T x) {
	if(x<0) {
		putchar('-');
		qprint(-x);
	} else if(x==0) {
		putchar('0');
		return;
	} else {
		if(x>=10) qprint(x/10);
		putchar('0'+x%10);
	}
}

int m;
struct qtype {
	int opt;
	string qs;//离线记录询问串 
	vector<int>subtr;//修改子树 
} q[maxn+5];

struct fenwick_tree {
	int sz;
	int c[maxn+5];
	inline int lowbit(int x) {
		return x&(-x);
	}
	void add(int x,int val) {
		for(int i=x; i<=sz; i+=lowbit(i)) c[i]+=val;
	}
	int sum(int x) {
		int ans=0;
		for(int i=x; i>0; i-=lowbit(i)) ans+=c[i];
		return ans;
	}
	int query(int l,int r) {
		return sum(r)-sum(l-1);
	}
} B;

struct SAM {
#define len(x) (t[x].len)
#define link(x) (t[x].link)
	struct node {
		int len;
		int link;
		int ch[maxc];
	} t[maxn+5];
	vector<int>E[maxn+5];
	const int root=1;
	int ptr=1;
	int extend(int last,int c) {
//		if(t[last].ch[c]&&len(last)+1==len(t[last].ch[c])) return t[last].ch[c];
		int p=last,cur=++ptr;
		len(cur)=len(last)+1;
		while(p&&!t[p].ch[c]) {
			t[p].ch[c]=cur;
			p=link(p);
		}
//		bool flag=0;
//		int clo;
		if(p==0) link(cur)=root;
		else {
			int q=t[p].ch[c];
			if(len(p)+1==len(q)) link(cur)=q;
			else {
//				if(p==last) flag=1;
				int clo=++ptr;
				link(clo)=link(q);
				len(clo)=len(p)+1;
				for(int i=0; i<maxc; i++) t[clo].ch[i]=t[q].ch[i];
				link(q)=link(cur)=clo;
				while(p&&t[p].ch[c]==q) {
					t[p].ch[c]=clo;
					p=link(p);
				}
			}
		}
		return cur;
	}

	int tim;
	int dfnl[maxn+5],dfnr[maxn+5];
	void get_dfn(int x,int f) {
		dfnl[x]=++tim;
		for(int i=0; i<(int)E[x].size(); i++) {
			int y=E[x][i];
			if(y!=f) get_dfn(y,x);
		}
		dfnr[x]=tim;
	}

	bool vis[maxn+5];
	ll strcnt=0;//本质不同子串个数
	void update(int x) {
		for(int y=x; y&&!vis[y]; y=link(y)) { //计算对子串个数的贡献
			vis[y]=1;
			strcnt+=len(y)-len(link(y));
		}
		B.add(dfnl[x],1);
	}
	ll query1() {
		return strcnt;
	}
	int query3(string &s) {
		int x=root;
		for(int i=0; i<(int)s.length(); i++) {
			int c=s[i]-'a';
			if(!t[x].ch[c]) return 0;
			x=t[x].ch[c];
		}
		return B.query(dfnl[x],dfnr[x]);
	}
	void build_fail() {
		for(int i=2; i<=ptr; i++) E[link(i)].push_back(i);
		get_dfn(1,0);
		B.sz=tim;
	}
} T1;

struct Trie {
	vector<pair<int,int> >E[maxn+5];
	void add_edge(int u,int v,int w) {
		E[u].push_back(make_pair(v,w));
		E[v].push_back(make_pair(u,w));
	}

	int pos[maxn+5];
	void bfs(int id,int s) {//bfs建广义SAM 
		static bool vis[maxn+5];
		queue<int>qu;
		qu.push(s);
		while(!qu.empty()) {
			int x=qu.front();
			qu.pop();
			vis[x]=1;
			if(x!=s) q[id].subtr.push_back(x);//根节点之前存在,不用更新 
			for(int i=0; i<(int)E[x].size(); i++) {
				int y=E[x][i].first;
				if(!vis[y]) {
					pos[y]=T1.extend(pos[x],E[x][i].second);
					qu.push(y);
				}
			}
		}
	}
} T2;



int main() {
	int id,n0,u,v,rt,sz;
	static char tmp[maxn+5];
	qread(id);
	qread(n0);
	for(int i=1; i<n0; i++) {
		qread(u);
		qread(v);
		scanf("%s",tmp);
		T2.add_edge(u,v,tmp[0]-'a');
	}
	T2.pos[1]=1;
	T2.bfs(0,1);

	qread(m);
	for(int i=1; i<=m; i++) {
		qread(q[i].opt);
		if(q[i].opt==2) {
			qread(rt);
			qread(sz);
			for(int j=1; j<sz; j++) {
				qread(u);
				qread(v);
				scanf("%s",tmp);
				T2.add_edge(u,v,tmp[0]-'a');
			}
			T2.bfs(i,rt);
		} else if(q[i].opt==3) {
			scanf("%s",tmp);
			q[i].qs=string(tmp);
		}
	}

	T1.build_fail();
	
	for(int j=0; j<(int)q[0].subtr.size(); j++) {//更新初始答案 
		int x=q[0].subtr[j];
		T1.update(T2.pos[x]);
	}
	for(int i=1; i<=m; i++) {
		if(q[i].opt==1) {
			qprint(T1.query1());
			putchar('
');
		} else if(q[i].opt==2) {
			for(int j=0; j<(int)q[i].subtr.size(); j++) {
				int x=q[i].subtr[j];
				T1.update(T2.pos[x]);
			}
		}else{
			qprint(T1.query3(q[i].qs));
			putchar('
');
		}
	}
}
原文地址:https://www.cnblogs.com/birchtree/p/12628554.html