[JZOJ5027]【NOI2017模拟3.25】历史行程
2017-03-29 22:08
405 查看
题目大意
给一个长度为n的01字符串s,还有m个询问,每个询问有两个数l,r,问s的前缀s[1..l],s[1..l+1]…s[1..r]中的任意两个前缀的最长公共后缀是多少。n,m<=100000
分析
首先可以把s反过来,这样就是问后缀的lcp了,询问记得也要反过来。考虑弄出一个sa。
对于一个询问[l,r],答案是什么呢?
就是rank[l]..rank[r]按大小排后,即l~r所有位置按照sa的顺序来排之后,每个相邻两个之间的lcp的最大值嘛。
即:
令a[1~r-l+1]=rank[l~r],
sort(a);
令height[i]为sa[i]与sa[i+1]的lcp
则ans=max(min(height[a[i]],height[a[i]+1]...height[a[i+1]−1]))
那么我们就学会了暴力。
学会了暴力,就上莫队吧。
现在问题来了,当我们新插入一个位置x之后,我们需要在原有的位置集合中,找到rank与rank[x]最相邻的两个位置,然后再更新答案。直接做的话怎么找都要带个log。
如何消去log?
机智法:
一个的结论:假如你有一个链表,维护一个元素左右两个相邻元素是谁。在删除了一些元素后,如果要回复原来的状态,按照原来删的顺序反过来加入,同时利用之前被删除的点的信息,可以o(1)恢复。如图:对于i,j,k三个点,假如我先删除了j,那么right[i]=right[j],left[k]=left[j],j的东西不管。
然后我要恢复j,那么只需要再次利用left[j],right[j],即可恢复原来状况。
推广到多个点,删除多次是一样的。但必须要按顺序来。
回到题目,设rans[i]表示当询问左端点在某一个询问块中,(设块尾位置为x),对于虚拟询问(x+1,i)的答案。
对于同一块内的询问,我们按右端点从大到小排,这样我们右端点就一直只会删除,为顺序提供保障。
我们对于一个询问:
1,假如它跟上一个询问的块不一样,我们把整一个链表重构,重构1~n所有元素,现在我们拥有一个l=1,r=n的区间[l,r]的信息。然后逐个删,左边l先删到块尾+1,右边r接着删到块尾+1,然后再重新加入右边至n,更新rans数组,最后再把l弄回块开头。
2,现在我们求答案。首先r调整至询问右端点。用rans[r]更新此询问的答案。现在相当于我把询问从块尾x切开,已经求了右边了,按理来说,此时询问的当前答案对应的是(x+1,r)这个询问的答案,我们只要把x到左端点逐个插回来更新就好了。我们l此时是在块开头,我们删除到结尾再加回去就可以求出来了。当然了,如果右端点已经小于x+1了,就只需要删除到右端点再插回去,rans也用不着了。
上面写得很繁杂,反正抓主线就是,我们要保证调整过的维护的序列,要能调回去再求其他询问的答案,即要严格保证插入删除顺序不会破坏原来链表的结构。
min(height[])用rmq搞就o(1)了。
总时间复杂度:O(nn√+nlog2n)
小细节
就是要注意询问的右端点小于块尾的情况。排序别排错了。SA要多用,有点生疏了。
代码
#include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; typedef long long ll; typedef double db; #define fo(i,j,k) for(i=j;i<=k;i++) #define fd(i,j,k) for(i=j;i>=k;i--) const int N=100005; struct rec { int l,r,bel,id; }que ; int sa ,rank ,height ,ws ,x ,y ,wv ,rmq [20]; int l,r,rans ,ans,prt ,le ,ri ,Log ; int i,j,k,n,m,siz,q,st,en,id; char s ; bool cmp(rec a,rec b) { return a.bel<b.bel||(a.bel==b.bel&&a.r>b.r); } void getsa() { fo(i,1,n) ws[x[i]]++; ws[2]+=ws[1]; fd(i,n,1) sa[ws[x[i]]--]=i; j=1; while (j<n) { q=0; fo(i,n-j+1,n) y[++q]=i; fo(i,1,n) if (sa[i]>j) y[++q]=sa[i]-j; fo(i,1,n) wv[i]=x[y[i]]; fo(i,1,n) ws[i]=0; fo(i,1,n) ws[x[i]]++; fo(i,1,n) ws[i]+=ws[i-1]; fd(i,n,1) sa[ws[wv[i]]--]=y[i]; fo(i,1,n) y[i]=x[i]; x[sa[1]]=1; q=1; fo(i,2,n) { if (y[sa[i]]!=y[sa[i-1]]||y[sa[i]+j]!=y[sa[i-1]+j]) q++; x[sa[i]]=q; } j*=2; } } void getheight() { fo(i,1,n) rank[sa[i]]=i; int k=0; fo(i,1,n) { if (k>0) k--; while (i+k<=n&&sa[rank[i]+1]+k<=n&&s[i+k]==s[sa[rank[i]+1]+k]) k++; height[rank[i]]=k; } } void getrmq() { fo(i,1,n) rmq[i][0]=height[i]; fo(j,1,Log ) fo(i,1,n) rmq[i][j]=min(rmq[i][j-1],rmq[i+(1<<(j-1))][j-1]); } int getmin(int x,int y) { if (x<=0||y<=0) return 0; int z=Log[y-x+1]; return min(rmq[x][z],rmq[y-(1<<z)+1][z]); } void restruct(int i) { fo(i,1,n) { le[sa[i]]=sa[i-1]; ri[sa[i]]=sa[i+1]; } l=1; r=n; for(;l<en+1;l++) { ri[le[l]]=ri[l]; le[ri[l]]=le[l]; } for(;r>en+1;r--) { ri[le[r]]=ri[r]; le[ri[r]]=le[r]; } rans[r]=0; for(;r<n;r++) { le[ri[r+1]]=r+1; ri[le[r+1]]=r+1; rans[r+1]=max(rans[r],max(getmin(rank[le[r+1]],rank[r+1]-1),getmin(rank[r+1],rank[ri[r+1]]-1))); } for(;l>st;l--) { le[ri[l-1]]=l-1; ri[le[l-1]]=l-1; } } int main() { freopen("history.in","r",stdin); freopen("history.out","w",stdout); scanf("%d %d\n",&n,&m); scanf("%s",s+1); fo(i,1,n/2) swap(s[i],s[n-i+1]); fo(i,1,n) x[i]=s[i]-'0'+1; siz=trunc(sqrt(n)); fo(i,1,m) { scanf("%d %d",&que[i].l,&que[i].r); r=n-que[i].l+1; que[i].l=n-que[i].r+1; que[i].r=r; que[i].bel=(que[i].l-1)/siz+1; que[i].id=i; } sort(que+1,que+1+m,cmp); fo(i,0,n) Log[i]=trunc(log(i)/log(2)); getsa(); getheight(); getrmq(); fo(i,1,m) { st=(que[i].bel-1)*siz+1; en=st+siz-1; id=que[i].id; if (que[i].bel!=que[i-1].bel) restruct(i); for(;r>que[i].r;r--) { ri[le[r]]=ri[r]; le[ri[r]]=le[r]; } for(;l<=en&&l<que[i].r;l++) { ri[le[l]]=ri[l]; le[ri[l]]=le[l]; } if (que[i].r>en)prt[id]=rans[que[i].r]; for(;l>st;l--) { if (l>que[i].l) prt[id]=max(prt[id],max(getmin(rank[le[l-1]],rank[l-1]-1),getmin(rank[l-1],rank[ri[l-1]]-1))); le[ri[l-1]]=l-1; ri[le[l-1]]=l-1; } } fo(i,1,m) printf("%d\n",prt[i]); }
相关文章推荐
- 【NOI2017模拟3.25】历史行程
- [JZOJ5029]. 【NOI2017模拟3.25】围墙
- 【JZOJ5037】【NOI2017模拟3.30】轮回
- 【JZOJ5043】【NOI2017模拟4.4】保持平衡
- 【JZOJ5037】【NOI2017模拟3.30】轮回
- [JZOJ100003]【NOI2017模拟.4.1】 Tree
- [JZOJ100004]【NOI2017模拟.4.1】 Dice
- JZOJ 5043. 【NOI2017模拟4.4】保持平衡
- 【JZOJ5046】【NOI2017模拟4.5】机器人游戏
- 【JZOJ5040】【NOI2017模拟4.2】押韵
- 【JZOJ5039】【NOI2017模拟4.2】查询
- [JZOJ5044]【NOI2017模拟4.4】Sone0
- [JZOJ100019] 【NOI2017模拟6.26】A
- [JZOJ5036]【NOI2017模拟3.30】原谅
- 【jzoj5290】【NOIP2017提高组A组模拟8.17】【行程的交集】
- jzoj 3947. 【省常中JSOI模拟】收历史作业 最长不下降子序列
- [JZOJ5037]【NOI2017模拟3.30】轮回
- 【JZOJ 100019】【NOI2017模拟6.26】A
- JZOJ 100019【NOI2017模拟6.26】A
- 5027. 历史行程