【BZOJ3881】[Coci2015]Divljak(AC自动机)(树上差分)(树状数组)

传送门


题解:

S S S建立AC自动机之后,我们发现一个 P P P串扔进来就是把所有自己匹配点的祖先不重复地+1,显然树上差分搞定,用树状数组维护一下标记之和就行了。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<22|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	inline int get_s(char *s){
		int len=0;char c;
		while(isspace(c=gc()));
		while(s[len++]=c,!isspace(c=gc())&&c!=EOF);
		s[len]='\0';return len;
	}
	
	template<typename T>
	inline T get(){
		char c;T num;
		while(!isdigit(c=gc()));num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline int gi(){return get<int>();}
}
using namespace IO;

using std::cerr;
using std::cout;

cs int N=2e6+7;

char s[N];int len;
int ps[N];

int son[N][26],fa[N],tot;

inline void ins(int id){
	len=get_s(s);int u=0;
	for(int re i=0;i<len;++i){
		int c=s[i]-'a';
		if(!son[u][c])son[u][c]=++tot;
		u=son[u][c];
	}ps[id]=u;
}

int el[N],nxt[N];
inline void adde(int u,int v){nxt[v]=el[u],el[u]=v;}

inline void build(){
	std::queue<int> q;
	for(int re i=0;i<26;++i)if(son[0][i])q.push(son[0][i]);
	while(!q.empty()){
		int u=q.front();q.pop();adde(fa[u],u);
		for(int re i=0;i<26;++i)
		son[u][i]?(q.push(son[u][i]),fa[son[u][i]]=son[fa[u]][i]):(son[u][i]=son[fa[u]][i]);
	}
}

int in[N],out[N],siz[N],d[N],ch[N],top[N],nd[N],dfn;

void dfs1(int u){
	siz[u]=1;ch[u]=-1;
	for(int re v=el[u];v;v=nxt[v]){
		d[v]=d[u]+1;dfs1(v);siz[u]+=siz[v];
		if(!~ch[u]||siz[ch[u]]<siz[v])ch[u]=v;
	}
}

void dfs2(int u,int tp){
	top[u]=tp;nd[in[u]=++dfn]=u;
	if(~ch[u])dfs2(ch[u],tp);
	for(int re v=el[u];v;v=nxt[v])if(v!=ch[u])dfs2(v,v);
	out[u]=dfn;
}

inline int LCA(int u,int v){
	while(top[u]!=top[v])d[top[u]]>d[top[v]]?u=fa[top[u]]:v=fa[top[v]];
	return d[u]<d[v]?u:v;
}

int tr[N];
inline void add(int p,int v){for(;p;p^=p&-p)tr[p]+=v;}
inline int query(int p){int res=0;for(;p<=dfn;p+=p&-p)res+=tr[p];return res;}
inline int qy(int u){return query(in[u])-query(out[u]+1);}

signed main(){
#ifdef zxyoi
	freopen("Divljak.in","r",stdin);
#endif
	int n=gi();
	for(int re i=1;i<=n;++i)ins(i);
	build();dfs1(0),dfs2(0,0);
	int q=gi();while(q--){
		switch(gi()){
			case 1:{
				len=get_s(s+1);int u=0;
				static int vl[N];
				for(int re i=1;i<=len;++i)
				vl[i]=in[u=son[u][s[i]-'a']];
				std::sort(vl+1,vl+len+1);
				add(vl[1],1);
				for(int re i=2;i<=len;++i){
					add(vl[i],1);
					if(i!=1)add(in[LCA(nd[vl[i-1]],nd[vl[i]])],-1);
				}
				break;
			}
			case 2:cout<<qy(ps[gi()])<<"\n";break;
		}
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值