zoukankan      html  css  js  c++  java
  • UOJ #219 BZOJ 4650 luogu P1117 [NOI2016]优秀的拆分 (后缀数组、ST表)

    连NOI Day1T1都不会做。。。看了题解都写不出来还要抄Claris的代码。。

    题目链接: (luogu)https://www.luogu.org/problemnew/show/P1117

    (bzoj)https://www.lydsy.com/JudgeOnline/problem.php?id=4650

    (uoj)http://uoj.ac/problem/219

    题解:

    (f[i])表示以(i)结束的(AA)型子串个数,(g[i])表示以(i)开始的(AA)型子串个数

    怎么求(f,g)?

    打破思维定势,谁说必须要一个一个求呢

    分长度来求

    枚举长度(L), 处理所有长度为(2L)(AA)型子串对(f)(g)的贡献

    如果每隔(L)的长度放一个打点计时器,呸,关键点

    那么任何(AA)型子串都会经过两个相邻关键点

    首先肯定要满足这两个关键位置上的字符一样

    在这个基础上求出往前往后最多多少个一样的

    这个就转化成了LCP和LCS问题,并且两个相邻关键点对(f,g)数组的影响是区间+1,使用差分前缀和解决

    然后推一推就行了,注意+1-1不要推错

    时间复杂度为调和级数,(O(nlog n))

    代码

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #define llong long long
    using namespace std;
    
    const int N = 1<<15;
    const int LGN = 15;
    const int S = 26;
    int log2[N+3];
    struct SparseTable
    {
    	int n;
    	int str[N+3];
    	int rk[N+3];
    	int tmp[N+3];
    	int height[N+3];
    	int h[N+3];
    	int sa[N+3];
    	int wb[N+3];
    	int mini[N+3][LGN+3];
    	void get_sa()
    	{
    		int *x = rk,*y = tmp;
    		for(int i=0; i<=S; i++) wb[i] = 0;
    		for(int i=1; i<=n; i++) wb[x[i]=str[i]]++;
    		for(int i=1; i<=S; i++) wb[i] += wb[i-1];
    		for(int i=n; i>=1; i--) sa[wb[x[i]]--] = i;
    		int s = S,p = 0;
    		for(int j=1; p<n; j<<=1)
    		{
    			p = 0;
    			for(int i=n-j+1; i<=n; i++) y[++p] = i;
    			for(int i=1; i<=n; i++) if(sa[i]>j) y[++p] = sa[i]-j;
    			for(int i=1; i<=s; i++) wb[i] = 0;
    			for(int i=1; i<=n; i++) wb[x[y[i]]]++;
    			for(int i=1; i<=s; i++) wb[i] += wb[i-1];
    			for(int i=n; i>=1; i--) sa[wb[x[y[i]]]--] = y[i];
    			swap(x,y);
    			p = 1; x[sa[1]] = 1;
    			for(int i=2; i<=n; i++) x[sa[i]] = (y[sa[i]]==y[sa[i-1]] && y[sa[i]+j]==y[sa[i-1]+j]) ? p : ++p;
    			s = p;
    		}
    		for(int i=1; i<=n; i++) rk[sa[i]] = i;
    		for(int i=1; i<=n; i++)
    		{
    			h[i] = h[i-1]==0 ? 0 : h[i-1]-1;
    			while(i+h[i]<=n && sa[rk[i-1]]+h[i]<=n && str[i+h[i]]==str[sa[rk[i]-1]+h[i]])
    			{
    				h[i]++;
    			}
    		}
    		for(int i=1; i<=n; i++) height[i] = h[sa[i]];
    		for(int i=1; i<=n; i++) mini[i][0] = height[i];
    		for(int j=1; j<=LGN; j++)
    		{
    			for(int i=1; i+(1<<j)-1<=n; i++)
    			{
    				mini[i][j] = min(mini[i][j-1],mini[i+(1<<j-1)][j-1]);
    			}
    		}
    	}
    	int querymin(int lb,int rb)
    	{
    		int g = log2[rb-lb+1];
    		return min(mini[lb][g],mini[rb-(1<<g)+1][g]);
    	}
    	int LCP(int x,int y)
    	{
    		if(x==y) return n-x+1;
    		if(rk[x]>rk[y]) swap(x,y);
    		return querymin(rk[x]+1,rk[y]);
    	}
    	void clear()
    	{
    		for(int i=1; i<=n; i++) str[i] = rk[i] = tmp[i] = height[i] = h[i] = sa[i] = wb[i] = 0;
    		for(int i=1; i<=n; i++)
    		{
    			for(int j=0; j<=LGN; j++)
    			{
    				mini[i][j] = 0;
    			}
    		}
    	}
    } s1,s2;
    llong f[N+3];
    llong g[N+3];
    char a[N+3];
    int n;
    
    int LCP(int x,int y) {return s1.LCP(x,y);}
    int LCS(int x,int y) {return s2.LCP(n+1-x,n+1-y);}
    
    void preprocess()
    {
    	log2[1] = 0; for(int i=2; i<=N; i++) log2[i] = log2[i>>1]+1;
    }
    
    void clear()
    {
    	s1.clear(); s2.clear();
    	for(int i=0; i<=n+1; i++) a[i] = 0,f[i] = g[i] = 0ll;
    }
    
    int main()
    {
    	preprocess();
    	int T; scanf("%d",&T);
    	while(T--)
    	{
    		scanf("%s",a+1); n = strlen(a+1); for(int i=1; i<=n; i++) a[i]-=96;
    		for(int i=1; i<=n; i++) s1.str[i] = a[i]; s1.n = n;
    		s1.get_sa();
    		for(int i=1; i<=n; i++) s2.str[i] = a[n+1-i]; s2.n = n;
    		s2.get_sa();
    		for(int i=1; i+i<=n; i++)
    		{
    			for(int j=i+i; j<=n; j+=i)
    			{
    				if(a[j]==a[j-i])
    				{
    					int lb = j-LCS(j,j-i)+1,rb = j+LCP(j,j-i)-1;
    					lb = max(lb+i-1,j); rb = min(rb,j+i-1);
    					if(lb<=rb)
    					{
    						f[lb]++; f[rb+1]--;
    						g[lb-i-i+1]++; g[rb+1-i-i+1]--;
    					}
    				}
    			}
    		}
    		for(int i=1; i<=n; i++) f[i] += f[i-1],g[i] += g[i-1];
    		llong ans = 0ll;
    		for(int i=1; i<n; i++)
    		{
    			llong tmp = f[i]*g[i+1];
    			ans += tmp;
    		}
    		printf("%lld
    ",ans);
    		clear();
    	}
    	return 0;
    }
    
  • 相关阅读:
    CentOS安装sctp协议
    视频编码未来简史
    Linux内核:分析coredump文件
    skb的两个函数pskb_copy和skb_copy
    《Linux内核设计与实现》读书笔记(十二)- 内存管理
    Linux内核学习笔记之seq_file接口创建可读写proc文件
    内核如何签名
    《女士品茶》与统计检验
    K近邻算法
    PCA原理分析
  • 原文地址:https://www.cnblogs.com/suncongbo/p/11032784.html
Copyright © 2011-2022 走看看