[NOI 2016] 优秀的拆分

题目传送-Luogu4117

题意:

\(T\)组数据,对于每组数据:
给你一个长度为\(n\)的字符串\(S\)
定义一个字符串\(t\)是好的,当且仅当它能被表示成\(aabb\)的形式,其中a和b都是字符串(可以相同)
\(S\)中有多少个子串是好的(本质相同位置不同也算不同)
\(T \le 10,n \le 30000\)

题解:

这题的构造方法极其巧妙,是道好题。
我们可以维护两个数组分别表示某个位置为结尾/开头的aa型字符串有多少个
那显然\(ans=\sum_{i=1}^{n-1}f_{i}*g_{i+1}\)(大概意思一下)
那考虑如何统计\(f\)\(g\)
我们枚举\(a\)的长度\(len\),并设置\(len,2*len,...,\lfloor \frac{n}{len}\rfloor *len\)为关键点,求出相邻关键点的\(LCP\)\(LCS\)(最长公共前/后缀),大概画幅图就知道对那些点有贡献,差分一下就可以得到\(f\)\(g\)了。

过程:

犯了个严重错误:见我的错题集LCA中的第1点
同时写代码的时候在变量代表的意义上纠结不清导致调题过程过长

代码:

const int N=30010;
int n;
char S[N];
int valf[N],valb[N];//i as end/start
int s_t[2][N<<2],t_s[2][N<<1],dep[2][N<<1],fa[2][N<<1],cur=0;
int head[N<<1],nxt[N<<1],to[N<<1],lst=0;
int NOW,ref[2][N];
inline void adde(int x,int y) {
	nxt[++lst]=head[x]; to[lst]=y; head[x]=lst;
}
namespace SAM {
	const int U=26;
	struct NODE {
		int tranc[U],dep,fa;
	}tre[N<<1];
	int las,rt,ind;
	inline int New_Node() {
		++ind; mem(tre[ind].tranc,0); tre[ind].dep=tre[ind].fa=0;
		return ind;
	}
	inline void Init() {
		mem(tre,0);
		cur=lst=0; mem(head,0);
		ind=0; las=rt=New_Node();
	}
	inline void Insert(int x) {
		// printf("test:%d\n",tre[1].tranc[1]);
		int p=las,np=New_Node(); tre[np].dep=tre[p].dep+1;
		for(;p && !tre[p].tranc[x];p=tre[p].fa) tre[p].tranc[x]=np;
		if(!p) {tre[np].fa=rt;}
		else {
			int q=tre[p].tranc[x];
			if(tre[p].dep+1==tre[q].dep) {tre[np].fa=q;}
			else {
				int nq=New_Node(); tre[nq].dep=tre[p].dep+1;
				memcpy(tre[nq].tranc,tre[q].tranc,sizeof(tre[q].tranc));
				tre[nq].fa=tre[q].fa; tre[q].fa=tre[np].fa=nq;
				for(;p && tre[p].tranc[x]==q;p=tre[p].fa) tre[p].tranc[x]=nq;
			}
		}
		las=np;
	}
	inline void Build() {
		for(int i=1;i<=ind;i++)
			if(tre[i].fa) adde(tre[i].fa,i);
	}
	void dfs(int u) {
		// printf("u=%d dep=%d\n",u,tre[u].dep);
		dep[NOW][u]=tre[u].dep; fa[NOW][u]=tre[u].fa;
		s_t[NOW][++cur]=u; t_s[NOW][u]=cur;
		for(int i=head[u];i;i=nxt[i]) {
			int v=to[i];
			if(v) {
				dfs(v);
				s_t[NOW][++cur]=u;
			}
		}
	}
}
inline void Get_SAM(char *s) {
	if(NOW) reverse(s+1,s+n+1);
	for(int i=1;i<=n;i++) {
		SAM::Insert(s[i]-'a');
		ref[NOW][NOW ? n-i+1 : i]=SAM::las;
	}
	SAM::Build(); SAM::dfs(SAM::rt);
}
namespace ST {
	const int lgN=18;
	int st[2][N<<2][lgN+3],lg[N<<2];
	inline void Set() {
		lg[1]=0;
		for(int i=2;i<=120000;i++)
			lg[i]=lg[i>>1]+1;
	}
	inline void Init() {
		mem(st,63);
	}
	inline int Comp(int x,int y,int c) {return dep[c][x]<dep[c][y] ? x : y;}
	inline void Get_ST() {
		// printf("???:%d %d\n",cur,SAM::ind*2-1);
		assert(cur==SAM::ind*2-1);
		// for(int i=1;i<=SAM::ind*2-1;i++) printf("%d ",s_t[NOW][i]); puts("");
		for(int i=1;i<=SAM::ind*2-1;i++) st[NOW][i][0]=s_t[NOW][i];
		// printf("%d\n",tot);
		for(int j=1;j<=lgN;j++)
			for(int i=1;i+(1<<j)-1<=SAM::ind*2-1;i++) {
				st[NOW][i][j]=Comp(st[NOW][i][j-1],st[NOW][i+(1<<(j-1))][j-1],NOW);
				// printf("%d %d %d:%d\n",c,i,j,st[c][i][j]);
			}
		// printf("st[1][8][1]=%d\n",st[1][8][1]);
	}
	inline int Query(int c,int x,int y) {
		// printf("%d %d %d %d %d dep[2]=%d\n",c,x,y,ref[c][x],ref[c][y],dep[1][2]);
		x=t_s[c][ref[c][x]],y=t_s[c][ref[c][y]];
		if(x>y) swap(x,y);
		// if(x>y) swap(x,y);
		int tmp=lg[y-x+1];
		// printf("%d %d %d\n",x,y,tmp);
		// printf("ask::%d\n",fa[c][Comp(st[c][x][tmp],st[c][y-(1<<tmp)+1][tmp])]);
		return dep[c][Comp(st[c][x][tmp],st[c][y-(1<<tmp)+1][tmp],c)];
	}
}
inline void Init() {
	// puts("?");
	mem(dep,0);
	SAM::Init(); ST::Init();
	mem(valf,0); mem(valb,0);
}
inline void Work() {

	Init();
	NOW=0; Get_SAM(S); ST::Get_ST(); SAM::Init();
	NOW=1; Get_SAM(S); ST::Get_ST(); SAM::Init();
	// printf("1 2 :%d\n",ST::Query(1,1,2));
	for(int len=1;len<=n;len++) {
		for(int st=1;st+len<=n;st+=len) {
			int lcp=ST::Query(1,st,st+len),lcs=ST::Query(0,st,st+len);
			// printf("%d %d lcp=%d lcs=%d\n",st,st+len,lcp,lcs);
			if(lcp+lcs-2>=len-1) {
				int s=max(st,st+len-lcs),t=min(st+len-1,st+lcp-1);//s=st+len-1-lcs+1
				// printf("%d %d\n",s,t);
				// printf("%d %d %d\n",len,s+len,t+len+1);
				++valf[s+len]; --valf[t+len+1];
				// printf("%d %d %d\n",len,s-len,t-len+1);
				++valb[s-len+1]; --valb[t-len+2];
			}
		}
	}
	for(int i=1;i<=n;i++)
		valf[i]+=valf[i-1];
	for(int i=1;i<=n;i++)
		valb[i]+=valb[i-1];
	ll ans=0;
	for(int i=2;i<n-1;i++)
		ans+=1ll*valf[i]*valb[i+1];
	printf("%lld\n",ans);
}
signed main() {
	ST::Set();
	int T; read(T);
	while(T--) {
		scanf("%s",S+1); n=strlen(S+1);
		Work();
	}
	return 0;
}

用时:3h

posted @ 2018-08-30 21:07  functionendless  阅读(212)  评论(0编辑  收藏  举报