zoukankan      html  css  js  c++  java
  • [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} floor *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
    ",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
    ",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
    ",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
    ",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
    ",c,i,j,st[c][i][j]);
    			}
    		// printf("st[1][8][1]=%d
    ",st[1][8][1]);
    	}
    	inline int Query(int c,int x,int y) {
    		// printf("%d %d %d %d %d dep[2]=%d
    ",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
    ",x,y,tmp);
    		// printf("ask::%d
    ",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
    ",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
    ",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
    ",s,t);
    				// printf("%d %d %d
    ",len,s+len,t+len+1);
    				++valf[s+len]; --valf[t+len+1];
    				// printf("%d %d %d
    ",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
    ",ans);
    }
    signed main() {
    	ST::Set();
    	int T; read(T);
    	while(T--) {
    		scanf("%s",S+1); n=strlen(S+1);
    		Work();
    	}
    	return 0;
    }
    

    用时:3h

  • 相关阅读:
    Java实现 LeetCode 394 字符串解码
    Java实现 LeetCode 394 字符串解码
    Java实现 LeetCode 392 判断子序列
    Java实现 LeetCode 392 判断子序列
    Java实现 LeetCode 392 判断子序列
    Java实现 LeetCode 391 完美矩形
    Java实现 LeetCode 391 完美矩形
    Java实现 LeetCode 391 完美矩形
    Java实现 LeetCode 390 消除游戏
    Java实现 LeetCode 390 消除游戏
  • 原文地址:https://www.cnblogs.com/functionendless/p/9562710.html
Copyright © 2011-2022 走看看