BZOJ4545: DQS的trie

题解:  在trie树上构造SAM 首先对于操作1  我们可以边加入边维护答案 通过增减dis[x]-dis[fa[i]]来得到 对于操作二 直接dfs建树即可 和原本构造trie树一样 对于操作三 用LCT维护 具有'价值'节点出现的次数 这个用LCT维护子树信息可以达到 然后就解决问题了

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <queue>
#include <cmath>
#include <set>
#include <map>
#define mp make_pair
#define pb push_back
#define pii pair<int,int>
#define link(x) for(edge *j=h[x];j;j=j->next)
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,r,l) for(int i=r;i>=l;i--)
const int MAXN=3e5+10;
const double eps=1e-8;
#define ll long long
using namespace std;
struct edge{int t;char v;edge*next;}e[MAXN<<1],*h[MAXN],*o=e;
void add(int x,int y,char ch){o->t=y;o->v=ch;o->next=h[x];h[x]=o++;}
ll read(){
	ll x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
	return x*f;
}
char str[MAXN];
int ch[MAXN][2],pre[MAXN],key[MAXN],vis[MAXN];//size zong sz xu
ll size[MAXN],sz[MAXN];
bool rt[MAXN],pis[MAXN];
int cur,cnt,n,m;
ll ans;
void newnode(int x){size[x]=key[x]=sz[x]=ch[x][0]=ch[x][1]=0;pre[x]=0;rt[x]=1;}
void up(int x){size[x]=size[ch[x][0]]+size[ch[x][1]]+sz[x]+key[x];}
void rotate(int x,int kind){
	int y=pre[x];
	pre[ch[x][kind]]=y;ch[y][!kind]=ch[x][kind];
	if(rt[y])rt[x]=1,rt[y]=0;
	else ch[pre[y]][ch[pre[y]][1]==y]=x;
	pre[x]=pre[y];ch[x][kind]=y;pre[y]=x;
	up(y);
}
void splay(int x){
	while(!rt[x]){
		if(rt[pre[x]])rotate(x,ch[pre[x]][0]==x);
		else{
			int y=pre[x];int kind=ch[pre[y]][0]==y;
			if(ch[y][kind]==x)rotate(x,!kind),rotate(x,kind);
			else rotate(y,kind),rotate(x,kind);
		}
	}
	up(x);
}
void access(int x){
	int y=0;
	while(x){
		splay(x);
		if(ch[x][1])pre[ch[x][1]]=x,rt[ch[x][1]]=1,sz[x]+=size[ch[x][1]];
		if(y)rt[y]=0,sz[x]-=size[y];
		ch[x][1]=y;up(x);
		y=x;x=pre[x];
	}
}
void destory(int u,int v){access(u);splay(v);ch[v][1]=pre[u]=0;rt[u]=1;up(v);}
void Link(int u,int v){
	access(u);splay(u);
	access(v);
	splay(v);
	pre[u]=v;sz[v]+=size[u];
}
ll querty(int u){
	access(u);splay(u);
	return size[u]-size[ch[u][0]];
}
int dis[MAXN],ch0[MAXN][26],fa[MAXN],f[MAXN];
int built(int x){
	int last=cur;cur=++cnt;int p=last;dis[cur]=dis[p]+1;newnode(cur);size[cur]=key[cur]=1;
	for(;p&&!ch0[p][x];p=fa[p])ch0[p][x]=cur;
	if(!p)fa[cur]=1,Link(cur,1),ans+=dis[cur]-dis[fa[cur]];
	else{
		int q=ch0[p][x];
		if(dis[q]==dis[p]+1)fa[cur]=q,Link(cur,q),ans+=dis[cur]-dis[q];
		else{
			int nt=++cnt;dis[nt]=dis[p]+1;newnode(nt);
			memcpy(ch0[nt],ch0[q],sizeof(ch0[q]));
			destory(q,fa[q]);ans-=dis[q]-dis[fa[q]];
			fa[nt]=fa[q];Link(nt,fa[q]);ans+=dis[nt]-dis[fa[nt]];
			fa[q]=fa[cur]=nt;Link(q,nt);Link(cur,nt);ans+=dis[q]-dis[nt];ans+=dis[cur]-dis[nt];
			for(;ch0[p][x]==q;p=fa[p])ch0[p][x]=nt;
		}
	}
	return cur;
}
void dfs(int x,int pre){
	f[x]=pre;
	link(x){
		if(j->t!=pre&&!pis[j->t]){
			pis[j->t]=1;cur=vis[x];vis[j->t]=built(j->v-'a');
			dfs(j->t,x);
		}
	}
}
ll slove(){
	scanf("%s",str);int len=strlen(str);
	int temp=1;
	for(int i=0;i<len;i++){
		int t=str[i]-'a';
		if(!ch0[temp][t])return 0;
		temp=ch0[temp][t];
	}
	return querty(temp);
}
int main(){
	read();vis[1]=1;newnode(1);cur=cnt=1;n=read();char ch;int u,v;ans=0;
	inc(i,1,n-1)u=read(),v=read(),scanf(" %c",&ch),add(u,v,ch),add(v,u,ch);
	dfs(1,0);
	m=read();int op,Rt,num;
	while(m--){
		op=read();
		if(op==1)printf("%lld\n",ans);
		else if(op==2){
			Rt=read();num=read();
			inc(i,1,num-1)u=read(),v=read(),scanf(" %c",&ch),add(u,v,ch),add(v,u,ch);
			dfs(Rt,f[Rt]);
		}
		else printf("%lld\n",slove());
	}
}

 

4545: DQS的trie

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 248  Solved: 90
[Submit][Status][Discuss]

Description

DQS的自家阳台上种着一棵颗粒饱满、颜色纯正的trie。
DQS的trie非常的奇特,它初始有n0个节点,n0-1条边,每条边上有一个字符。并且,它拥有极强的生长力:某个i时刻,某个节点就会新生长出一颗子树,它拥有si个节点且节点之间的边上有一个字符,并且新生长出来的子树也是一个树结构。然而因为是新长出来的,根据生活常识可知si必定不会大于i时刻之前的树的大小。
DQS定义trie的子串为从根节点(1号节点)往下走到所有节点所构成的字符串的所有的后缀。DQS身为一个单身doge,常常取出其中一个子串送给妹子,然而他并不希望送给妹子两个相同的子串,所以他非常关心当前trie的本质不同的子串数目。
DQS有时还会去商店购买子串,若他在商店看上某个子串,他希望得知这个子串是否在自家阳台的trie上已经出现,若出现则出现了多少次。如果出现了,他就可以直接回家取trie上的子串辣!
然而DQS身为一个蒟蒻,看着自家阳台的trie树一天天在长大,他被如此众多的节点弄得眼花缭乱,于是他找到了IOI2016Au的你。他会告诉你自家trie树的成长历程,他希望你能够对于每一次询问都做出正确回复。
 

 

Input

第一行输入一个整数id,代表测试点编号。
接下来一行输入一个整数n0,表示初始树的大小。
接下来n0-1行,每行两个整数u,v和一个字符c,表示u号节点和v号节点之间有一条边,边上的字母为c。
接下来输入m表示有m组操作。
对于每一组,第一行输入一个整数opt。
若opt=1,则是一组询问,询问当前trie的本质不同的子串数目是多少。
若opt=2,则后面跟两个整数rt,si,表示以点rt为根向下长出一个子树,大小为si。
接下来si-1行,每行两个整数u,v和一个字符c,表示u号节点和v号节点之间有一条边,边上的字母为c。若长出子树之前当前树的大小是n,则这si-1点的编号分别为n+1,n+2…n+si-1。
若opt=3,则是一组询问,后面输入一个字符串S,询问字符串S在当前trie中的出现次数。 
 

 

Output

对于每个opt=1或3,输出一行表示答案。
 

 

Sample Input

1
4
1 2 a
1 3 b
2 4 b
6
1
2 2 4
2 5 b
2 6 c
5 7 b
1
3 ab
2 6 3
6 8 a
6 9 b
1

Sample Output

3
7
2
11
posted @ 2018-09-12 19:43  wang9897  阅读(212)  评论(0编辑  收藏  举报