BZOJ1921: [Ctsc2010]珠宝商(点分治+SAM)

这篇博客介绍了如何解决BZOJ1921题目的珠宝商问题,通过点分治策略,当点数大于等于n时利用后缀树在O(sze+m)的时间复杂度内处理每个位置的开始和结束点,以统计答案。若点数不足则采用暴力扩展路径的方法,总的时间复杂度为O(n^2)。
摘要由CSDN通过智能技术生成

传送门

题解:
点分治,如果点数 ≥ n \ge \sqrt{n} n ,则结合后缀树 O ( s z e + m ) O(sze+m) O(sze+m)处理出每个位置的开始,结束点并统计答案。否则从每个点开始暴力扩展路径。

时间复杂度 O ( n n ) O(n \sqrt{n}) O(nn )

#include <bits/stdc++.h>
using namespace std;
typedef pair <int,int> pii;
typedef long long LL;

inline int rd() {
	char ch=getchar(); int i=0,f=1;
	while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
	while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=getchar();}
	return i*f;
}

const int N=1e5+50, B=150;
int n,m,mx,total,G,fa[N];
int sze[N],c1[N],c2[N],vis[N];
char s[N];
long long ans;
vector <int> edge[N];

struct sam {
	char ch[N];
	int last,tot;
	int pos[N],son[N][26],tr[N][26],fail[N],len[N];
	int c[N],q[N];
	vector <int> edge[N];
	sam() {last=tot=1;}
	inline void extend(int c,int op) {
		int p=++tot; len[p]=len[last]+1; pos[p]=op;
		for(;last && !son[last][c];last=fail[last]) son[last][c]=p;
		if(!last) fail[p]=1;
		else {
			int q=son[last][c];
			if(len[q]==len[last]+1) fail[p]=q;
			else {
				int np=++tot; len[np]=len[last]+1; pos[np]=pos[q];
				memcpy(son[np],son[q],sizeof(son[np]));
				fail[np]=fail[q]; fail[q]=fail[p]=np;
				for(;last && son[last][c]==q;last=fail[last]) son[last][c]=np;
			}
		} last=p;
	}
	inline void init() {
		for(int i=1;i<=m;i++) extend(ch[i]-'a',i);
		for(int i=1;i<=tot;i++) if(fail[i]) edge[fail[i]].push_back(i);
		for(int i=1;i<=tot;i++) ++c[len[i]];
		for(int i=1;i<=tot;i++) c[i]+=c[i-1];
		for(int i=tot;i>=1;i--) q[c[len[i]]--]=i;
		memset(c,0,sizeof(c));
		for(int i=tot;i>=1;i--) {
			int u=q[i];
			if(pos[u]==len[u]) ++c[u];
			if(fail[u]) c[fail[u]]+=c[u];
		}
		for(int i=1;i<=tot;i++) {
			int u=q[i];
			for(int e=0,v;e<edge[u].size() && (v=edge[u][e]);++e)
				tr[u][ch[pos[v]-len[u]]-'a']=v;
		}
		memset(q,0,sizeof(q));
	}
	inline pii trans(pii p,char c) {
		if(!p.first) return pii(0,0);
		else if(p.second==len[p.first]) return pii(tr[p.first][c-'a'],p.second+1);
		else {
			if(ch[pos[p.first]-p.second]==c) return pii(p.first,p.second+1);
			else return pii(0,0);
		}
	}
	inline int trans2(int p,char c) {return son[p][c-'a'];}
	inline void add(pii p) {++q[p.first];}
} ori,rev;

inline void calcG(int x,int f) {
	sze[x]=1;  int mx_son=0;
	for(int e=0,v;e<edge[x].size() && (v=edge[x][e]);++e)
		if(!vis[v] && v!=f) {
			calcG(v,x);
			sze[x]+=sze[v];
			mx_son=max(mx_son,sze[v]);
		}
	mx_son=max(mx_son,total-sze[x]);
	if(mx_son<=mx) mx=mx_son, G=x;
}

inline void dfs(int x,int f,pii p1,pii p2) {
	p1=ori.trans(p1,s[x]); p2=rev.trans(p2,s[x]);
	ori.add(p1); rev.add(p2); sze[x]=1;
	for(int e=0,v;e<edge[x].size() && (v=edge[x][e]);++e)	if(!vis[v] && v!=f) {
		dfs(v,x,p1,p2);		
		sze[x]+=sze[v];
	}
}
inline void dfs(sam &t,int x,int sum,int flag) {
	sum+=t.q[x];
	if(t.pos[x]==t.len[x]) (flag ? c2[m-t.pos[x]+1] : c1[t.pos[x]])+=sum;;
	for(int e=0,v;e<t.edge[x].size() && (v=t.edge[x][e]);++e) dfs(t,v,sum,flag);	
}

int rt;
inline void dfs3(int x,int f,int p,int flag) {
	if(!p) return;
	ans+=flag*ori.c[p];
	for(int e=0,v;e<edge[x].size() && (v=edge[x][e]);++e) if(!vis[v] && v!=f) dfs3(v,x,ori.trans2(p,s[v]),flag);
}
inline void dfs2(int x,int f,int flag) {
	int p=1;
	if(flag<0) {
		for(int i=x;;i=fa[i]) {p=ori.trans2(p,s[i]); if(i==rt) break;}
		p=ori.trans2(p,s[G]); 
	} 
	if(flag<0) dfs3(rt,0,ori.trans2(p,s[rt]),flag);
	else dfs3(x,0,ori.trans2(p,s[x]),flag);
	for(int e=0,v;e<edge[x].size() && (v=edge[x][e]);++e) if(!vis[v] && v!=f) dfs2(v,x,flag); 
}
inline void force_solve(int x,int flag) {
	rt=x; dfs2(x,0,flag);
}
inline void calc1(int t) {
	for(int i=1;i<=m;i++) c1[i]=c2[i]=0;
	dfs(ori,1,0,0); 
	dfs(rev,1,0,1);
	for(int i=1;i<=m;i++) ans+=(LL)c1[i]*c2[i]*t;
	for(int i=1;i<=ori.tot;i++) ori.q[i]=0;
	for(int i=1;i<=rev.tot;i++) rev.q[i]=0;
}
inline void calc2(int x) {
	if(sze[x]<=B) {
		force_solve(x,-1);
	} else {
		pii p(1,0);
		dfs(x,G,ori.trans(p,s[G]),rev.trans(p,s[G]));
		calc1(-1);
	}
}
inline void dfs_pre(int x,int f) {fa[x]=f; for(int e=0,v;e<edge[x].size() && (v=edge[x][e]);++e) if(!vis[v] && v!=f) dfs_pre(v,x);}
inline void solve(int x) {
	if(total<=B) force_solve(x,1);
	else {
		pii p(1,0); vis[x]=1;
		ori.add(ori.trans(p,s[x]));
		rev.add(rev.trans(p,s[x]));
		for(int e=0,v;e<edge[x].size() && (v=edge[x][e]);++e)
			if(!vis[v]) dfs_pre(v,x), dfs(v,x,ori.trans(p,s[x]),rev.trans(p,s[x]));
		calc1(1);
		for(int e=0,v;e<edge[x].size() && (v=edge[x][e]);++e)
			if(!vis[v]) calc2(v);
		for(int e=0,v;e<edge[x].size() && (v=edge[x][e]);++e) if(!vis[v]) {
			mx=total=sze[v];
			calcG(v,x);
			solve(G);
		}
	}
}
int main() {
	n=rd(), m=rd();
	for(int i=1;i<n;i++) {
		int x=rd(), y=rd();
		edge[x].push_back(y);
		edge[y].push_back(x);
	} 
	scanf("%s",s+1);
	scanf("%s",ori.ch+1);
	reverse_copy(ori.ch+1,ori.ch+m+1,rev.ch+1);
	ori.init(); rev.init();
	
	mx=total=n; 
	calcG(1,0);
	solve(G);
	
	cout<<ans<<'\n';
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值