• P1117 [NOI2016]优秀的拆分


    题意

    这题正解还是有点难想的。

    显然满足条件的字符串就是两个(AA)样子的串一前一后。

    (a_i)表示以(i)为开头的(AA)串长度,(a_i)表示以(i)为结尾的(AA)串长度,那么答案显然为:
    (sumlimits_{i=1}^{n-1}a_{i+1}*b_i)

    于是考虑怎么求这个,我们考虑枚举(AA)的长度(len),对原串每隔(len)设一个关键点,因为长为(len)(AA)串必定过且只过两个关键点,因此对于每对相邻的关键点,我们求出它们随对应的(AA)串。

    求出(lcp)表示([c_{i},n])([c_{i+1},n])(c_i)表示第(i)个关键点)的最长公共前缀,(lcs)表示([1,c_{i-1}-1])([1,c_{i+1}-1])的最长公共后缀。

    (lcs+lcp<len)时,如下图:

    我们发现并不会有(AA)串过它们。

    (lcp+lcsgeqslant len)时:

    我们设(t=lcp-lcs-len+1)

    我们发现在前面的(t)长度的点都可以作为一个(AA)串的开头,后面(t)长度的点都可以作为一个(AA)串的结尾。

    于是我们要区间加(1),这个差分就好了。

    code:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=30010;
    int T,n;
    int a[maxn],b[maxn],c[maxn],lg[maxn];
    ll ans;
    char s[maxn];
    struct SA
    {
    	int n,num;
    	int sa[maxn],rk[maxn],oldrk[maxn],id[maxn],tmpid[maxn],cnt[maxn];
    	int height[maxn][20];
    	char s[maxn];
    	inline void clear()
    	{
    		memset(sa,0,sizeof(sa));
    		memset(rk,0,sizeof(rk));
    		memset(height,0x3f,sizeof(height));
    		memset(s,0,sizeof(s));//一定要清空字符串。
    	}
    	inline bool cmp(int x,int y,int k){return oldrk[x]==oldrk[y]&&oldrk[x+k]==oldrk[y+k];}
    	inline void build()
    	{
    		num=300;
    		memset(cnt,0,sizeof(cnt));
    		for(int i=1;i<=n;i++)cnt[rk[i]=s[i]]++;
    		for(int i=1;i<=num;i++)cnt[i]+=cnt[i-1];
    		for(int i=n;i;i--)sa[cnt[rk[i]]--]=i;
    		for(int t=1;t<=n;t<<=1)
    		{
    			int tot=0;
    			for(int i=n-t+1;i<=n;i++)id[++tot]=i;
    			for(int i=1;i<=n;i++)if(sa[i]>t)id[++tot]=sa[i]-t;
    			tot=0;
    			memset(cnt,0,sizeof(cnt));
    			for(int i=1;i<=n;i++)cnt[tmpid[i]=rk[id[i]]]++;
    			for(int i=1;i<=num;i++)cnt[i]+=cnt[i-1];
    			for(int i=n;i;i--)sa[cnt[tmpid[i]]--]=id[i];
    			memcpy(oldrk,rk,sizeof(rk));
    			for(int i=1;i<=n;i++)rk[sa[i]]=cmp(sa[i-1],sa[i],t)?tot:++tot;
    			num=tot;
    			if(num>=n)break;
    		}
    		for(int i=1,j=0;i<=n;i++)
    		{
    			if(j)j--;
    			while(s[i+j]==s[sa[rk[i]-1]+j])j++;
    			height[rk[i]][0]=j;
    		}
    		for(int j=1;j<=18;j++)
    			for(int i=1;i+(1<<j)-1<=n;i++)
    				height[i][j]=min(height[i][j-1],height[i+(1<<(j-1))][j-1]);
    	}
    	inline int query(int x,int y)
    	{
    		x=rk[x],y=rk[y];
    		if(x>y)swap(x,y);x++;
    		int t=lg[y-x+1];
    		return min(height[x][t],height[y-(1<<t)+1][t]);
    	}
    }Sa[2];
    inline void init()
    {
    	memset(a,0,sizeof(a));
    	memset(b,0,sizeof(b));
    	Sa[0].clear(),Sa[1].clear();
    	ans=0;
    }
    inline void solve()
    {
    	scanf("%s",s+1);n=strlen(s+1);
    	Sa[0].n=Sa[1].n=n;
    	for(int i=1;i<=n;i++)Sa[0].s[i]=Sa[1].s[n-i+1]=s[i];
    	Sa[0].build(),Sa[1].build();
    	for(int len=1;len<=n/2;len++)
    	{
    		int tot=0;
    		for(int i=len;i<=n;i+=len)c[++tot]=i;
    		for(int i=1;i<tot;i++)
    		{
    			int lcp=min(Sa[0].query(c[i],c[i+1]),len),lcs=min(Sa[1].query(n-c[i]+2,n-c[i+1]+2),len-1);
    			if(lcp+lcs<len)continue;
    			int t=lcp+lcs-len+1;
    			a[c[i]-lcs]++,a[c[i]-lcs+t]--;
    			b[c[i+1]+lcp-t]++,b[c[i+1]+lcp]--;
    		}
    	}
    	for(int i=1;i<=n;i++)a[i]+=a[i-1],b[i]+=b[i-1];
    	for(int i=1;i<n;i++)ans+=1ll*a[i+1]*b[i];
    	printf("%lld
    ",ans);
    }
    int main()
    {
    	//freopen("test.in","r",stdin);
    	//freopen("test.out","w",stdout);
    	lg[0]=-1;
    	for(int i=1;i<=30000;i++)lg[i]=lg[i>>1]+1;
    	scanf("%d",&T);
    	while(T--)
    	{
    		init();
    		solve();
    	}
    	return 0;
    }
    
  • 相关阅读:
    [APIO2012]派遣
    Luogu_2774 方格取数问题
    Luogu4149 [IOI2011]Race
    Luogu_4886 快递员
    Loj_6282. 数列分块入门 6
    LOJ 6281 数列分块入门 5
    三维偏序 cdq
    【逆序对】 模板
    【luogu P1637 三元上升子序列】 题解
    【luogu P3609 [USACO17JAN]Hoof, Paper, Scissor蹄子剪刀布】 题解
  • 原文地址:https://www.cnblogs.com/nofind/p/12082455.html
Copyright © 2020-2023  润新知