BZOJ 4516: [Sdoi2016]生成魔咒——后缀数组、并查集
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4516
题意
一开始串为空,每次往串后面加一个字符,求本质不同的子串的个数,可以离线。即长度为N的字符串,对于每一个前缀,求本质不同的子串的个数。(字符集为int)
做法
首先,我们把所有的数字离散化。然后考虑后缀数组,我们把字符串倒过来,于是很神奇地,往最后加字符变成了添加一个后缀。
我们知道,在求出SA之后,一个字符串的本质不同的子串的个数等于(子串的个数)-(重复计数的个数)等于\(\frac{N*(N+1)}{2}-\sum height[i]\)。
那么,我们可以先把整个(倒过来的)字符串的SA求出来,然后尝试模拟插入后缀这个过程。
每插入一个后缀(插入的位置是这个后缀的rank),在所有已经插入的后缀中,找到它的前驱和后继。又因为两个后缀的LCP,即重复计数的子串个数等于height上的区间最小值,所以我们可以在\(O(1)\)的时间去用height上的最小值更新答案。
实现
这个当然是可以用ST表+线段树/平衡树维护的,但是因为可以离线(求后缀数组本来就要求离线...),我们有更简单的做法。
考虑从后往前做,将所有插入操作变成删除操作。当我们删除一个后缀时,只需要维护它在未删除的后缀中的前驱、后继到它之间的最小值即可(实际上并不需要显式地求出前驱后继)。
可以用双向链表/并查集实现。
我写了并查集的做法:将每个后缀看成一条边,连接的点代表两个后缀之间的LCP(height),删除后缀时连上相应的边,在unite()的时候维护min值。
其他
并查集也是可以离线求前驱后继的,在unite()的时候维护该段最左、最右点即可。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=100050, INF=0x3f3f3f3f;
void rd(int &x){
x=0; int ch=getchar();
while(ch<'0'||'9'<ch) ch=getchar();
while('0'<=ch&&ch<='9') x=x*10+ch-'0', ch=getchar();
}
int N, M, up;
int w[MAXN], rk[MAXN], ht[MAXN], sa[MAXN], c[MAXN];
int fa[MAXN], sz[MAXN], mn[MAXN], tmp[MAXN];
ll sum, ans[MAXN];
inline void chkmn(int &x, int y){if(x>y)x=y;}
void init(){
for(int i=0; i<=N; ++i) fa[i]=i, mn[i]=ht[i];
}
int find(int x){return x==fa[x]?x:(fa[x]=find(fa[x]));}
void unite(int x, int y){
x=find(x); y=find(y);
if(x==y) return;
if(sz[x]>sz[y]) swap(x,y);
fa[x]=y;
sum+=max(mn[x],mn[y]);
chkmn(mn[y],mn[x]);
}
inline int wcmp(int *x, int a, int b, int k){
return x[a]==x[b]&&x[a+k]==x[b+k];
}
inline void rsort(int *x, int *y){
memset(c, 0, sizeof(c));
for(int i=0; i<N; ++i) c[x[i]]++;
for(int i=1; i<up; ++i) c[i]+=c[i-1];
for(int i=N-1; i>=0; --i) sa[--c[x[y[i]]]]=y[i];
}
void getsa(){
int *x=rk, *y=ht;
for(int i=0; i<N; ++i) x[i]=w[i], y[i]=i;
rsort(x,y);
for(int k=1, p=0; p<N; k<<=1, up=p){
p=0;
for(int i=N-k; i<N; ++i) y[p++]=i;
for(int i=0; i<N; ++i) if(sa[i]>=k) y[p++]=sa[i]-k;
rsort(x,y); swap(x,y); p=0; x[sa[0]]=p++;
for(int i=1; i<N; ++i)
if(wcmp(y,sa[i],sa[i-1],k)) x[sa[i]]=p-1;
else x[sa[i]]=p++;
}
for(int i=0; i<N; ++i) rk[sa[i]]=i;
ht[0]=0;
for(int i=0, j, p=0; i<N-1; ++i){
for((p?p--:0),j=sa[rk[i]-1];w[i+p]==w[j+p];++p);
ht[rk[i]]=p;
}
}
int main(){
rd(N);
for(int i=1; i<=N; ++i) rd(w[N-i]), tmp[M++]=w[N-i];
sort(tmp,tmp+M); M=unique(tmp,tmp+M)-tmp;
for(int i=0; i<N; ++i) w[i]=lower_bound(tmp,tmp+M,w[i])-tmp+1;
M=N++; up=N+1; getsa();
sum=(ll)M*(M+1)/2;
for(int i=0; i<N; ++i) sum-=ht[i];
ans[N]=sum; init();
for(int i=0; i<M; ++i){
sum-=M-i;
unite(rk[i],rk[i]+1);
ans[M-i]=sum;
}
for(int i=2; i<=N; ++i) printf("%lld\n", ans[i]);
return 0;
}
BZOJ 4516: [Sdoi2016]生成魔咒——后缀数组、并查集的更多相关文章
- BZOJ.4516.[SDOI2016]生成魔咒(后缀数组 RMQ)
题目链接 后缀自动机做法见这(超好写啊). 后缀数组是可以做的: 本质不同的字符串的个数为 \(子串个数-\sum_{ht[i]}\),即 \(\frac{n(n+1)}{2}-\sum_{ht[i] ...
- BZOJ 4516: [Sdoi2016]生成魔咒(后缀数组)
传送门 解题思路 题目其实就是动态维护本质不同的串的个数.考虑到只有加数字的操作,所以可以用后缀数组.题目是每次往后加数字,这样不好处理,因为每次加数字之后所有的后缀都会改变.所以要转化一下思路,就是 ...
- BZOJ 4516: [Sdoi2016]生成魔咒 [后缀自动机]
4516: [Sdoi2016]生成魔咒 题意:询问一个字符串每个前缀有多少不同的子串 做了一下SDOI2016R1D2,题好水啊随便AK 强行开map上SAM 每个状态的贡献就是\(Max(s)-M ...
- BZOJ.4516.[SDOI2016]生成魔咒(后缀自动机 map)
题目链接 后缀数组做法见这. 直接SAM+map.对于每个节点其产生的不同子串数为len[i]-len[fa[i]]. //15932kb 676ms #include <map> #in ...
- BZOJ 4516: [Sdoi2016]生成魔咒 后缀自动机 性质
http://www.lydsy.com/JudgeOnline/problem.php?id=4516 http://blog.csdn.net/doyouseeman/article/detail ...
- BZOJ 4516 [Sdoi2016]生成魔咒 ——后缀自动机
本质不同的字串,考虑SA的做法,比较弱,貌似不会. 好吧,只好用SAM了,由于后缀自动机的状态最简的性质, 所有不同的字串就是∑l[i]-l[fa[i]], 然后后缀自动机是可以在线的,然后维护一下就 ...
- [SDOI2016] 生成魔咒 - 后缀数组,平衡树,STL,时间倒流
[SDOI2016] 生成魔咒 Description 初态串为空,每次在末尾追加一个字符,动态维护本质不同的子串数. Solution 考虑时间倒流,并将串反转,则变为每次从开头删掉一个字符,即每次 ...
- BZOJ 4516. [Sdoi2016]生成魔咒【SAM 动态维护不同子串数量】
[Sdoi2016]生成魔咒 动态维护不同子串的数量 想想如果只要查询一次要怎么做,那就是计算各个点的\(len[u]-len[link[u]]\)然后求和即可,现在要求动态更新,我们可以保存一个答案 ...
- 【bzoj4516】[Sdoi2016]生成魔咒 后缀数组+倍增RMQ+STL-set
题目描述 魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示.例如可以将魔咒字符 1.2 拼凑起来形成一个魔咒串 [1,2].一个魔咒串 S 的非空字串被称为魔咒串 S 的生成魔咒. 例如 S=[1,2 ...
随机推荐
- dashboard and reporting Interface analysis
dashboard and reporting Interface analysis > show system show system backup show system counters ...
- [洛谷P3250][HNOI2016]网络
题目大意:给定一棵树.有三种操作: $0\;u\;v\;t:$在$u$到$v$的链上进行重要度为$t$的数据传输. $1\;x:$结束第$x$个数据传输. $2\;x:$询问不经过点$x$的数据传输中 ...
- [GDOI2014]拯救莫莉斯 状压DP
题面: 莫莉斯·乔是圣域里一个叱咤风云的人物,他凭借着自身超强的经济头脑,牢牢控制了圣域的石油市场. 圣域的地图可以看成是一个n*m的矩阵.每个整数坐标点(x , y)表示一座城市( 1\le x\l ...
- LOJ6388:[THUPC2018]赛艇——题解
https://loj.ac/problem/6388 如果你做过BZOJ5217:[Lydsy2017省队十连测]航海舰队的话,那么恭喜你,这道题就是大水题. 如果你做过BZOJ4259:残缺的字符 ...
- BZOJ2242 [SDOI2011]计算器 【BSGS】
2242: [SDOI2011]计算器 Time Limit: 10 Sec Memory Limit: 512 MB Submit: 4741 Solved: 1796 [Submit][Sta ...
- 51nod 1215 数组的宽度&poj 2796 Feel Good(单调栈)
单调栈求每个数在哪些区间是最值的经典操作. 把数一个一个丢进单调栈,弹出的时候[st[top-1]+1,i-1]这段区间就是弹出的数为最值的区间. poj2796 弹出的时候更新答案即可 #inclu ...
- 【极值问题】【CF1063B】 Labyrinth
传送门 Description 给你一个\(n~\times~m\)的矩阵,一开始你在第\(r\)行第\(c\)列.你的上下移动不受限制,向左最多移动\(x\)次,向右最多移动\(y\)次.求你最多能 ...
- C. Line (扩展欧几里得)
C. Line time limit per test 1 second memory limit per test 256 megabytes input standard input output ...
- oracle xml操作
/*=====================生成\修改xml========================= */ --xmlelement多个标签层级 SELECT XMLELEMENT(& ...
- Eclipse中安装插件的三种方式
转载自:http://www.cnblogs.com/lcgustc/archive/2013/01/03/2843496.html Eclipse也用了很久,但是都没有怎么去研究过怎么安装插件,今天 ...