• [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

  • 相关阅读:
    【BZOJ 2124】【CodeVS 1283】等差子序列
    【BZOJ 1036】【ZJOI 2008】树的统计Count
    【BZOJ 1901】【ZJU 2112】Dynamic Rankings
    【BZOJ 3924】【ZJOI 2015】幻想乡战略游戏
    【BZOJ 4103】【THUSC 2015】异或运算
    【BZOJ 4513】【SDOI 2016】储能表
    【HDU 3622】Bomb Game
    【BZOJ 3166】【HEOI 2013】Alo
    【BZOJ 3530】【SDOI 2014】数数
    【BZOJ 4567】【SCOI 2016】背单词
  • 原文地址:https://www.cnblogs.com/functionendless/p/9562710.html
Copyright © 2020-2023  润新知