zoukankan      html  css  js  c++  java
  • 【洛谷P4070】生成魔咒

    题目

    题目链接:https://www.luogu.com.cn/problem/P4070
    魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示。例如可以将魔咒字符 (1,2) 拼凑起来形成一个魔咒串 ([1,2])
    一个魔咒串 (S) 的非空字串被称为魔咒串 (S) 的生成魔咒。
    例如 (S=[1,2,1]) 时,它的生成魔咒有 ([1],[2],[1,2],[2,1],[1,2,1]) 五种。(S=[1,1,1]) 时,它的生成魔咒有 ([1],[1,1],[1,1,1]) 三种,最初 S 为空串。
    共进行 (n) 次操作,每次操作是在 (S) 的结尾加入一个魔咒字符。每次操作后都需要求出,当前的魔咒串 (S) 共有多少种生成魔咒。

    思路

    考虑在加入第 (i) 个数字之后会有多少种新的子串生成。显然数量等于 (i) 减有多少个子串之前出现过。
    那么我们只需要找到一个 (j(j<i)) 使得 (i)(j) 的 LCS 尽量长即可。假设 LCS 长度为 (l),显然贡献为 (i-l)
    那么就把字符串反过来跑一遍 SA,然后从后往前枚举数字,用一个 set 维护之前枚举过的所有数字的 (rank),在 set 上 lower_bound 即可找到两个与 (rank[i]) 最近的位置,用较大的 LCP 去更新即可。
    时间复杂度 (O(nlog n))

    代码

    #include <bits/stdc++.h>
    #define AK =
    #define IOI 233
    using namespace std;
    typedef long long ll;
    
    const int N=100010,LG=18,Inf=1e9;
    int n,m,s[N],sa[N],x[N],y[N],c[N],rk[N],height[N],lg[N],rmq[N][LG+1];
    ll ans,QuantAsk;
    map<int,int> b;
    set<int> vis;
    
    void SA()
    {
    	for (int i=1;i<=n;i++) x[i]=s[i],c[x[i]]++;
    	for (int i=2;i<=m;i++) c[i]+=c[i-1];
    	for (int i=n;i>=1;i--) sa[c[x[i]]--]=i;
    	for (int k=1;k<=n;k<<=1)
    	{
    		int num=0;
    		for (int i=n-k+1;i<=n;i++) y[++num]=i;
    		for (int i=1;i<=n;i++) if (sa[i]>k) y[++num]=sa[i]-k;
    		for (int i=1;i<=m;i++) c[i]=0;
    		for (int i=1;i<=n;i++) c[x[i]]++;
    		for (int i=2;i<=m;i++) c[i]+=c[i-1];
    		for (int i=n;i>=1;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0;
    		swap(x,y);
    		x[sa[1]]=num=1;
    		for (int i=2;i<=n;i++)
    			x[sa[i]]=(y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k]) ? num : ++num;
    		m=num;
    		if (n==m) break;
    	}
    }
    
    void geth()
    {
    	for (int i=1;i<=n;i++) rk[sa[i]]=i;
    	for (int i=1,k=0;i<=n;i++)
    	{
    		if (k) k--;
    		int j=sa[rk[i]-1];
    		while (s[j+k]==s[i+k]) k++;
    		height[rk[i]]=k;
    	}
    }
    
    void getst()
    {
    	for (int i=1;i<=n;i++) rmq[i][0]=height[i];
    	for (int j=1;j<=LG;j++)
    		for (int i=1;i+(1<<j)-1<=n;i++)
    			rmq[i][j]=min(rmq[i][j-1],rmq[i+(1<<(j-1))][j-1]);
    }
    
    int LCP(int i,int j)
    {
    	int t=lg[j-i];
    	return min(rmq[i+1][t],rmq[j-(1<<t)+1][t]);
    }
    
    int main()
    {
    	scanf("%d",&n);
    	for (int i=n;i>=1;i--)
    	{
    		scanf("%d",&s[i]);
    		if (b.find(s[i])==b.end()) b[s[i]]=++m;
    		s[i]=b[s[i]];
    	}
    	SA(); geth(); getst();
    	vis.insert(-Inf); vis.insert(Inf);
    	lg[1]=0;
    	for (int i=2;i<=n;i++)
    		lg[i]=lg[i>>1]+1;
    	for (int i=n;i>=1;i--)
    	{
    		ans+=n-i+1;
    		int p=*--vis.upper_bound(rk[i]),q=*vis.lower_bound(rk[i]);
    		if (p==-Inf && q==Inf) QuantAsk AK IOI;
    		if (p!=-Inf && q==Inf) ans-=LCP(p,rk[i]);
    		if (p==-Inf && q!=Inf) ans-=LCP(rk[i],q);
    		if (p!=-Inf && q!=Inf) ans-=max(LCP(p,rk[i]),LCP(rk[i],q));
    		printf("%lld
    ",ans);
    		vis.insert(rk[i]);
    	}
    	if (QuantAsk AK IOI) return 0;
    		else return -1;
    }
    
  • 相关阅读:
    android的HTTP框架之Volley
    android学习笔记五。2、其他组件
    android学习笔记四
    android学习笔记二、Activity深入学习
    android事件学习
    android之handler机制深入解析
    java线程深入学习
    K-Means
    git fetch + merge与 git pull的区别
    git分支管理
  • 原文地址:https://www.cnblogs.com/stoorz/p/14050257.html
Copyright © 2011-2022 走看看