冲刺NOI2017 (24) A (后缀数组 回滚莫队)
来源:互联网 发布:sql统计表字段数 编辑:程序博客网 时间:2024/06/06 02:20
题目大意
给出一个长度为
题解
因为要在原串的前缀上分析后缀十分的别扭,所以对这个问题略作转化:将原串翻转过来,求左端点在区间
问题已经转化为了:左端点在区间
只分析到这里的话,对于每个询问可以
但是这样的复杂度是非常不可观的,分析后缀间lcp的性质:若有三个左端点分别为
有了上面的性质,虽然无法直接优化上面的做法,但是将所有的询问离线后这个性质就显得很有用了。考虑将所有询问离线后莫队,对于每个添加操作,就相当于在原有的一堆散点中插入了一个散点,新插入的点两侧的点原有的答案就一定不比新点与两侧的答案优,这些过程用几个多重集进行即可。对于每个删除操作就相当于添加操作的逆操作。这样做的时间复杂度为
考虑如何去掉这个log。发现添加操作是无法去掉log的,那么我们就干脆去掉添加操作,用只带删除的回滚莫队来解决这个问题。考虑用删除操作时用双向链表维护,每删除一个位置就修改其前驱和后继元素的指针并更新答案。这样就可以去掉维护位置的log了,因为虽然修改的次数很多,但是对修改后的答案进行的查询只有q个,所以考虑用分块维护链表中相邻位置的两后缀的lcp的长度,这样就可以每次
回滚莫队见这里。
代码
#include <cstdio>#include <iostream>//#include <ctime>#include <algorithm>using namespace std;inline int read() { register int val=0; char ch; while(~(ch=getchar()) && (ch<'0' || ch>'9')); val=ch-'0'; while(~(ch=getchar()) && (ch>='0' && ch<='9')) val=(val<<1)+(val<<3)+ch-'0'; return val;}const int maxn=int(1e5)+111;int n,m;char s[maxn];int siz, num, bel[maxn];int ans[maxn];struct Query { int l,r,id; Query() {} Query(int a,int b,int c):l(a),r(b),id(c) {} bool operator < (const Query &b) const { return bel[l]==bel[b.l]?r>b.r:bel[l]<bel[b.l]; }}q[maxn];void Read() { register int i; n=read(), m=read(); scanf("%s",s); reverse(s,s+n); while(siz*siz<n) ++siz; for(i=1;i<=n;++i) bel[i]=(i-1)/siz+1; num=bel[n]; int l,r; for(i=1;i<=m;++i) { l=read(), r=read(); q[i]=Query(n-r+1,n-l+1,i); } sort(q+1,q+1+m); return;}int K;int sa[maxn], rnk[maxn], tmp[maxn];int h[maxn];int st[maxn][20];inline bool cmp(const int &a,const int &b) { if(rnk[a]!=rnk[b]) return rnk[a]<rnk[b]; else { int x=a+K<n?rnk[a+K]:-1; int y=b+K<n?rnk[b+K]:-1; return x<y; }}void Init_sa() { register int i; for(i=0;i<=n;++i) { sa[i]=i; rnk[i]=i<n?s[i]:-1; } for(K=1;K<=n;K<<=1) { sort(sa,sa+1+n,cmp); tmp[sa[0]]=1; for(i=1;i<=n;++i) if(cmp(sa[i-1],sa[i])) tmp[sa[i]]=tmp[sa[i-1]]+1; else tmp[sa[i]]=tmp[sa[i-1]]; for(i=0;i<=n;++i) rnk[i]=tmp[i]; } return;}void Init_lcp(int *lcp) { register int i, j, h=0; for(i=0;i<=n;++i) { rnk[sa[i]]=i; lcp[i]=0; } lcp[sa[0]]=0; for(i=0;i<n;++i) { j=sa[rnk[i]-1]; if(h>0) --h; while(i+h<n && j+h<n && s[i+h]==s[j+h]) ++h; lcp[rnk[j]]=h; } return;}#define bin(k) (1<<(k))int lg2[maxn];void Init_lg2() { register int i,j; for(i=1,j=0;i<maxn;i<<=1,++j) lg2[i]=j; for(i=3;i<maxn;++i) if(!lg2[i]) lg2[i]=lg2[i-1]; return;}void Init_st() { register int i,j; for(i=1;i<=n;++i) st[i][0]=h[i]; int upp=lg2[n]; for(i=1;i<=upp;++i) for(j=1;j+bin(i)-1<=n;++j) st[j][i]=min(st[j][i-1],st[j+bin(i-1)][i-1]); return;}inline int RMQ(const int &l,const int &r) { int lg=lg2[r-l+1]; return min(st[l][lg],st[r-bin(lg)+1][lg]);}void Init() { Init_lg2(); Init_sa(); Init_lcp(h); Init_st(); return;}bool used[maxn];int pre[maxn], nex[maxn];int cnt[maxn], bcnt[400];int getmax() { register int i,j; for(i=num;!bcnt[i];--i); for(j=i*siz;!cnt[j];--j); return j;}void pre_calc(int id) { register int i; for(i=0;i<=n;++i) { used[i]=false; cnt[i]=bcnt[bel[i]]=pre[i]=nex[i]=0; } for(i=(id-1)*siz+1;i<=n;++i) used[rnk[i-1]]=true; int cur, last=0; for(i=1;i<=n;++i) if(used[i]) { pre[i]=last; nex[last]=i; if(last) ++cnt[cur=RMQ(last,i-1)], ++bcnt[bel[cur]]; last=i; } nex[last]=n+1; return;}#define mp make_pairpair<int,int> s1[maxn], s2[maxn], s3[maxn]; //pre, nex, cntint t1,t2,t3;void Del(int id,int sign) { id=rnk[id-1]; int cur, tot=0; if(pre[id]>=1) { cur=RMQ(pre[id],id-1); ++tot; if(sign) s3[++t3]=mp(cur,-1); --cnt[cur], --bcnt[bel[cur]]; if(sign) s2[++t2]=mp(pre[id],nex[pre[id]]); nex[pre[id]]=nex[id]; } if(nex[id]<=n) { cur=RMQ(id,nex[id]-1); ++tot; if(sign) s3[++t3]=mp(cur,-1); --cnt[cur], --bcnt[bel[cur]]; if(sign) s1[++t1]=mp(nex[id],pre[nex[id]]); pre[nex[id]]=pre[id]; } if(tot==2) { cur=RMQ(pre[id],nex[id]-1); if(sign) s3[++t3]=mp(cur,1); ++cnt[cur], ++bcnt[bel[cur]]; } return;}void Rollback() { while(t1) { pre[s1[t1].first]=s1[t1].second; --t1; } while(t2) { nex[s2[t2].first]=s2[t2].second; --t2; } while(t3) { cnt[s3[t3].first]-=s3[t3].second; bcnt[bel[s3[t3].first]]-=s3[t3].second; --t3; } return;}int Mo(int pos,int id) { register int i=pos, ql=(id-1)*siz+1, qr=n; pre_calc(id); for(i=pos;bel[q[i].l]==id;++i) { while(qr>q[i].r) Del(qr--,0); while(ql<q[i].l) Del(ql++,1); ans[q[i].id]=getmax(); Rollback(); ql=(id-1)*siz+1; } return i;}void Solve() { register int i,pos=1; for(i=1;i<=num;++i) pos=Mo(pos,i); return;}int main() {// freopen("a.in","r",stdin);// freopen("a.out","w",stdout);// double t1=clock(); Read(); Init(); Solve(); for(int i=1;i<=m;++i) printf("%d\n",ans[i]);// printf("%.3lfsec\n",(clock()-t1)/CLOCKS_PER_SEC); return 0;}
- 冲刺NOI2017 (24) A (后缀数组 回滚莫队)
- 冲刺NOI2017 (20) 距离 (可持久化树链剖分)
- 冲刺NOI2017 (22) 养猫 (线性规划方程转网络流)
- 冲刺NOI2017 (20) 苹果树 (矩阵树定理 容斥原理 Meet in middle)
- 【NOI2017模拟6.26】A
- [JZOJ100019] 【NOI2017模拟6.26】A
- 100019. 【NOI2017模拟6.26】A
- 后缀数组(倍增)
- 后缀数组(详细)
- 后缀数组(详细)
- 后缀数组(转载)
- 后缀数组(不完善)
- 后缀数组(poj3729)
- suffix_array(后缀数组)
- poj3581_Sequence(后缀数组)
- 后缀数组(一)
- bzoj1031(后缀数组)
- POJ1743(后缀数组)
- 线性回归与逻辑回归思考
- 解决无法将java项目部署到tomcat中去,在公司电脑里的项目回家。。
- 转]C语言图形编程(五) -二维图形变换①
- 测试Unity不同层级下的脚本调用顺序
- jsp中Session和Cookie 的区别
- 冲刺NOI2017 (24) A (后缀数组 回滚莫队)
- POJ 1818 ATP 笔记
- TestNG 中决定测试案例的执行顺序最好的办法
- Java Script-Lesson02
- Preparation
- hIve 和hbase的区别
- 基于时间序列模型的预测
- CSS样式设置
- 6.22周赛 Elevator 电梯模拟