CF1276F Asterisk Substrings

前言

不得不说,这个题真的难,但是在我的不懈坚持之下还是被我给调出来了。

这就是专属于我们的快乐吧,在你做出题的那一刻,欣喜若狂。

解法

统计本质不同的字串数,第一眼看上去是懵的,不知道该从哪里下手,甚至想用广义SAM。

但是其实不用那么麻烦。

我们将子串分为四类,S,S,S,ST,最后算上空集和

你发现这里面最难搞的就是S*T,前面的直接用最普通的求法求就好了。

就是用当前及节点的len减去parent树上的爹的len,后缀自动机基操吧。

但是楼下好像并没有这样做,我也不知道为什么是对的?????

那么我们就直接去统计S*T。

你会发现这里,结束节点集合相同的S所对应的T是完全相同的。

但是我们无法直接找到这样的集合,不过,parent树上全都是这样的。

这就启发我们在parent树上进行合并,具体是求链并的长度。

这个可以自行百度,所谓链并,就是求一堆点的到lca的路径上点的集合。

链并长度就是这个集合的大小

链并的长度就是对于当前S的T的种类数,直接乘上就行了

LCA我是树剖实现的,链并可以用线段树维护。

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define int long long
#define ll long long
const int N=2e5+5;
char ch[N];
ll ans;
int to[N],nxt[N],head[N],rp;
void add_edg(int x,int y){
	to[++rp]=y;nxt[rp]=head[x];head[x]=rp;
}
int fa[N],dep[N],siz[N],son[N],top[N];
int dfn[N],cnt,idf[N];
int lon[N];
int LCA(int x,int y){
	x=idf[x];y=idf[y];
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		x=fa[top[x]];
	}
	return dep[x]<dep[y]?x:y;
}
struct XDS{
	int sum[N*40],ls[N*40],rs[N*40];
	int seg,al[N*40],ar[N*40];
	void pushup(int x){
		if(ls[x])al[x]=al[ls[x]];
		else if(rs[x])al[x]=al[rs[x]];
		if(rs[x])ar[x]=ar[rs[x]];
		else if(ls[x])ar[x]=ar[ls[x]];
		if(ls[x]&&rs[x])
			sum[x]=sum[ls[x]]+sum[rs[x]]-lon[LCA(ar[ls[x]],al[rs[x]])];
		else if(ls[x])sum[x]=sum[ls[x]];
		else if(rs[x])sum[x]=sum[rs[x]];
		return ;
	}
	void ins(int &x,int l,int r,int pos){
		if(!x)x=++seg;
		if(l==r){
			al[x]=ar[x]=pos;
			sum[x]=lon[idf[pos]];
			//cout<<sum[x]<<" "<<idf[pos]<<" "<<pos<<endl;
			return ;
		}
		int mid=l+r>>1;
		if(pos<=mid)ins(ls[x],l,mid,pos);
		else ins(rs[x],mid+1,r,pos);
		pushup(x);return ;
	}
	int merge(int x,int y){
		if(!x||!y)return x+y;
		ls[x]=merge(ls[x],ls[y]);
		rs[x]=merge(rs[x],rs[y]);
		pushup(x);
		return x;
	}
}xds;
int rt[N];
struct SAM{
	struct node{
		int fail,son[27];
		int len;
		node(){};
	}tr[N];
	int pos[N],seg,las;
	SAM(){seg=las=1;}
	void dfs_fi(int x){
		siz[x]=1;son[x]=0;
		lon[x]=tr[x].len;
		for(re i=head[x];i;i=nxt[i]){
			int y=to[i];fa[y]=x;dep[y]=dep[x]+1;
			dfs_fi(y);siz[x]+=siz[y];
			if(!son[x]||siz[y]>siz[son[x]])son[x]=y;
		}
	}
	void dfs_se(int x,int f){
		top[x]=f;dfn[x]=++cnt;idf[cnt]=x;
		if(son[x])dfs_se(son[x],f);
		for(re i=head[x];i;i=nxt[i]){
			int y=to[i];
			if(y==son[x])continue;
			dfs_se(y,y);
		}
	}
	void ins(int c,int id){
		int p=las,np=++seg;las=np;
		tr[np].len=tr[p].len+1;pos[id]=seg;
		while(p&&!tr[p].son[c])tr[p].son[c]=np,p=tr[p].fail;
		if(!p)tr[np].fail=1;
		else {
			int q=tr[p].son[c];
			if(tr[q].len==tr[p].len+1)tr[np].fail=q;
			else {
				int nq=++seg;tr[nq]=tr[q];
				tr[nq].len=tr[p].len+1;
				tr[q].fail=tr[np].fail=nq;
				while(p&&tr[p].son[c]==q)tr[p].son[c]=nq,p=tr[p].fail;
			}
		}
	}
	void get_slpf(){
		for(re i=2;i<=seg;i++)
			add_edg(tr[i].fail,i);//cout<<tr[i].fail<<" "<<i<<endl;
		dfs_fi(1);dfs_se(1,1);
	}
	void sol(int x){
		for(re i=head[x];i;i=nxt[i]){
			int y=to[i];
			sol(y);
			rt[x]=xds.merge(rt[x],rt[y]);
		}
		if(x!=1){
			ans+=1ll*(tr[x].len-tr[tr[x].fail].len)*xds.sum[rt[x]];
		}
		//cout<<x<<" "<<ans<<endl;
	}
	void get_edg(){
		memset(head,0,sizeof(head));rp=0;
		for(re i=2;i<=seg;i++)add_edg(tr[i].fail,i);
	}
}be,af;
signed main(){
	scanf("%s",ch+1);
	int len=strlen(ch+1);
	for(re i=1;i<=len;i++)be.ins(ch[i]-'a',i);
	for(re i=len;i>=1;i--)af.ins(ch[i]-'a',i);
	for(re i=2;i<=be.seg;i++){
		if(be.tr[i].len!=len)ans+=be.tr[i].len-be.tr[be.tr[i].fail].len;
		ans+=be.tr[i].len-be.tr[be.tr[i].fail].len;
	//S和*S
	for(re i=2;i<=af.seg;i++)//T和*T
		if(af.tr[i].len!=len)ans+=af.tr[i].len-af.tr[af.tr[i].fail].len;
	af.get_slpf();
	for(re i=1;i<=len-2;i++)
		xds.ins(rt[be.pos[i]],1,cnt,dfn[af.pos[i+2]]);
	be.get_edg();
	be.sol(1);
	printf("%lld",ans+2);
}
posted @ 2021-08-08 06:36  fengwu2005  阅读(58)  评论(0编辑  收藏  举报