【SDOI2016】生成魔咒

Description

魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示。例如可以将魔咒字符 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 共有多少种生成魔咒。

Input

第一行一个整数 n。 
第二行 n 个数,第 i 个数表示第 i 次操作加入的魔咒字符。 
1≤n≤100000。,用来表示魔咒字符的数字 x 满足 1≤x≤10^9

Output

输出 n 行,每行一个数。第 i 行的数表示第 i 次操作后 S 的生成魔咒数量

Sample Input


1 2 3 3 3 1 2

Sample Output





12 
17 
22

Hint

数据约束: 
对于 10% 的数据,1≤n≤10。 
对于 30% 的数据,1≤n≤100。 
对于 60% 的数据,1≤n≤1000。 
对于 100% 的数据,1≤n≤100000。 
用来表示魔咒字符的数字 x 满足 1≤x≤109。

反过来后,每多一个原来的前缀,相当于多一个现在的后缀.
注意到一个事(tao)实(lu).

每次新加入的那个后缀与前面加的后缀的lcp一定是前面名次与他最相近的两个后缀与他的lcp的最大值,

因为名次越近相似度越高.

所以维护一个set和ST表,每次求出这个值,最终产生的答案就是这个后缀的长度-lcp.


 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstdlib>
 4 #include<cstring>
 5 #include<string>
 6 #include<algorithm>
 7 #include<map>
 8 #include<complex>
 9 #include<queue>
10 #include<stack>
11 #include<cmath>
12 #include<set>
13 #include<vector>
14 #define LL long long
15 #define maxn 100010
16 using namespace std;
17 int s[maxn],sa[maxn],rk[maxn],tmp[maxn],n,k;
18 LL lcp[maxn],ans[maxn],f[maxn][21];
19 set<int>se;
20 bool cmp(int i,int j){
21   if(rk[i]!=rk[j]) return rk[i]<rk[j];
22   else {
23     int ri=i+k<=n?rk[i+k]:-1,rj=j+k<=n?rk[j+k]:-1;
24     return ri<rj;
25   }
26 }
27 void get_sa(){
28   for(int i=0;i<=n;i++)
29     sa[i]=i,rk[i]=i<n?s[i]:-1;
30   for(k=1;k<=n;k*=2){
31     sort(sa,sa+n+1,cmp);
32     tmp[sa[0]]=0;
33     for(int i=1;i<=n;i++)
34       tmp[sa[i]]=tmp[sa[i-1]]+(cmp(sa[i-1],sa[i])?1:0);
35     for(int i=0;i<=n;i++)
36       rk[i]=tmp[i];
37   }
38 }
39 void get_lcp(){
40   for(int i=0;i<=n;i++) rk[sa[i]]=i;
41   int h=0;lcp[0]=0;
42   for(int i=0;i<n;i++){
43     int j=sa[rk[i]-1];
44     if(h>0) h--;
45     for(;j+h<n && i+h<n;h++)
46       if(s[j+h]!=s[i+h]) break;
47     lcp[rk[i]-1]=h;
48   }
49 }
50 int main(){
51  // freopen("!.in","r",stdin);
52  // freopen("!.out","w",stdout);
53   scanf("%d",&n);
54   for(int i=n-1;i>=0;i--)
55     scanf("%d",&s[i]);
56   get_sa();get_lcp();
57   for(int i=0;i<=n;i++) f[i][0]=lcp[i];
58   for(int j=1;j<=18;j++)
59     for(int i=0;i+(1<<(j-1))<=n;i++)
60       f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
61   se.insert(rk[n-1]);
62   ans[n-1]=1;
63   for(int i=n-2;i>=0;i--){
64     int lcpa=0,lcpb=0;
65     se.insert(rk[i]);
66     set<int>::iterator it=se.find(rk[i]);
67     if(it!=se.begin()){
68       it--;
69       int len=log(rk[i]-*it)/log(2);
70       if(1<<(len+1)<=rk[i]-*it) len++;
71       lcpa=min(f[*it][len],f[rk[i]-(1<<len)][len]);
72       it++;
73     }
74     if((++it)!=se.end()){
75       int len=log(*it-rk[i])/log(2);
76       if(1<<(len+1)<=rk[i]-*it) len++;
77       lcpb=min(f[rk[i]][len],f[*it-(1<<len)][len]);
78     }
79     ans[i]=n-i-max(lcpa,lcpb);
80   }
81   for(int i=n-1;i>=0;i--)
82     ans[i]+=ans[i+1],printf("%lld\n",ans[i]);
83   return 0;
84 }
				
时间: 2024-10-05 17:40:21

【SDOI2016】生成魔咒的相关文章

[Sdoi2016]生成魔咒[SAM or SA]

4516: [Sdoi2016]生成魔咒 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1017  Solved: 569[Submit][Status][Discuss] Description 魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示.例如可以将魔咒字符 1.2 拼凑起来形成一个魔咒串 [1,2]. 一个魔咒串 S 的非空字串被称为魔咒串 S 的生成魔咒. 例如 S=[1,2,1] 时,它的生成魔咒有 [1].[2].[1,2].[2

BZOJ4516: [Sdoi2016]生成魔咒 后缀自动机

#include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<cmath> #include<algorithm> #include<cstdlib> #include<map> #define N 200005 #define ll long long using namespace std; int read()

bzoj4516: [Sdoi2016]生成魔咒(SAM)

4516: [Sdoi2016]生成魔咒 题目:传送门 题解: 真奥义之SAM裸题... 其实对于当前新增节点x的操作,每次对ans的贡献就是dep[x]-dep[fail[x]](根据fail指针的定义随便YY) 然后有思路之后乍看题目每个x是10^9...瞬间GG %了已发cc然后被D飞,直接上map啊 代码: 1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cmath

P4070 [SDOI2016]生成魔咒

题目地址:P4070 [SDOI2016]生成魔咒 相信看到题目之后很多人跟我的思路是一样的-- 肯定要用 SA(P3809 [模板]后缀排序) 肯定要会求本质不同的子串个数(P2408 不同子串个数) 然后?就不会了...... 瓶颈在哪儿? 你会发现每往后添加一个字符,整个 sa 数组只会插入一个数,要维护不难 但是 height 会无规律变化,这就导致无法高效维护 怎么办呢? 倒置字符串 我们将整个字符串倒置过来 显然本质不同的子串个数不会变化 而每往前添加一个字符串, height 的变

[SDOI2016] 生成魔咒 - 后缀数组,平衡树,STL,时间倒流

[SDOI2016] 生成魔咒 Description 初态串为空,每次在末尾追加一个字符,动态维护本质不同的子串数. Solution 考虑时间倒流,并将串反转,则变为每次从开头删掉一个字符,即每次从后缀集合中删掉一个后缀. 预处理出后缀数组和高度数组后,用平衡树维护所有后缀集合(按照后缀排序),要删除一个后缀 \(S[sa[p],n]\) 时,找到它在平衡树上的前驱 \(u\) 和后继 \(v\) ,如果都存在,那么这一步的贡献就是 \[(n-sa[p]+1) - Max(h[p],h[v]

Bzoj4516 [Sdoi2016]生成魔咒

Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 947  Solved: 529 Description 魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示.例如可以将魔咒字符 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].[

BZOJ 4516: [Sdoi2016]生成魔咒

Description 给出一串数字,求每次插入一个数字后本质不同的子串. Sol SAM. 在 SAM 上添加节点的时候统计一下 \(val[np]-val[par[np]]\) 就可以了... 用 map 存一下边,复杂度 \(O(nlogn)\) Code /************************************************************** Problem: 4516 User: BeiYu Language: C++ Result: Accept

[SDOI2016]生成魔咒

OJ题号: BZOJ4516 题目大意: 按顺序在一个序列的末尾插入数字,每次求出插入后能得到的本质不同的子串个数. 思路: 每次在SAM后加入这个数字,每次新出现的本质不同的子串个数就等于new_p->len-new_p->link->len. 由于数字范围比较大,可以考虑离散化或者map. 事实上也可以用hash,不过实践证明会比map还慢很多,内存也浪费很多. 另外需要注意开long long. 1 #include<map> 2 #include<cstdio&

●BZOJ 4516 [Sdoi2016]生成魔咒

题链: http://www.lydsy.com/JudgeOnline/problem.php?id=4516 题解: 把串反过来后,问题变为求每个后缀的互不相同的子串个数.首先用倍增算法求出 sa[],rank[],height[],然后对 height[]数组建立 ST表.接着求出整个串的子串个数,ans+=N-sa[i]-height[i].(我从0开始编号的)式子的含义就是考虑每个后缀相比它的前一名,多了几个与之前不同的且串头为该后缀的头的子串. (一定要清晰地懂得并理解那个式子哦)

LibreOJ #2033. 「SDOI2016」生成魔咒

二次联通门 : LibreOJ #2033. 「SDOI2016」生成魔咒 /* LibreOJ #2033. 「SDOI2016」生成魔咒 调了整整一天啊... 绝望啊 最后发现是1打成i了啊!!! */ #include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <map> const int BUF = 10000020; char