【NOI2017模拟3.25】历史行程
2017-03-29 20:55
375 查看
Description
给出一个长度为n的字符串,m次询问前缀l~r中两两的最长后缀最长是多少。n,m<=1e5
Solution
显然先倒过来,询问变成求后缀的lcpSA处理一下就好了。
但是rank不是有序的,无法处理,怎么办呢?
可以离线莫队,维护一个set,每次插入或删除都可以log n解决。
但是复杂度过高会T怎么办呢?
我们每次插入就是要求某个点的前继和后继。
这个东西可以用双向链表来维护。
但是链表如何O(1)插入?
我们会发现,如果我们按照之前删除的倒序插入,那么插入的复杂度就是O(1)的。
于是我们处理出询问左端点所在块的左端点到询问右端点的链表。
这个可以通过把右端点从大到小排序来实现。
因为左端点就是在块内变化的,所以每次我们把左端点删到这个块的右端点,然后再加回去,这样链表是不会发生变化的。
于是我们就强行把维护前后继的log给去掉了=w=
具体实现看代码应该能懂吧。。。
Code
#include <cmath> #include <cstdio> #include <cstring> #include <algorithm> #define fo(i,a,b) for(int i=a;i<=b;i++) #define fd(i,a,b) for(int i=a;i>=b;i--) using namespace std; const int N=1e5+5,M=400; int sa[N],rank[N],height[N],w[N],x[N],y[N],mi[18],f[N][18],lg ; int n,m,l,r,tot,L ,R ,a ,b ,pre ,suf ,an ,ans; char st ; struct note{ int l,r,id; friend bool operator < (note x,note y) { return b[x.l]<b[y.l]||b[x.l]==b[y.l]&&x.r>y.r; } }ask ; void tsort() { memset(w,0,sizeof(w));int mx=0; fo(i,1,n) w[x[y[i]]]++,mx=max(mx,x[y[i]]); fo(i,1,mx) w[i]+=w[i-1]; fd(i,n,1) sa[w[x[y[i]]]--]=y[i]; } void get_sa() { fo(i,1,n) y[i]=i;tsort(); for(int j=1;j<=n;j=j*2) { int k=0;fo(i,n-j+1,n) y[++k]=i; fo(i,1,n) if (sa[i]>j) y[++k]=sa[i]-j; tsort(); fo(i,1,n) y[i]=x[i],x[i]=0; x[sa[1]]=k=1; fo(i,2,n) { if (y[sa[i]]!=y[sa[i-1]]||y[sa[i]+j]!=y[sa[i-1]+j]) k++; x[sa[i]]=k; } } fo(i,1,n) rank[sa[i]]=i; } void get_height() { int k=0; fo(i,1,n) { if (k) k--; int j=sa[rank[i]-1]; while (j+k<=n&&i+k<=n&&st[j+k]==st[i+k]) k++; height[rank[i]]=k; } } void get_rmq() { mi[0]=1;int len=log(n)/log(2); fo(i,1,len) mi[i]=mi[i-1]*2; fo(i,1,n) f[i][0]=height[i],lg[i]=log(i)/log(2); fo(j,1,len) fo(i,1,n-mi[j]+1) f[i][j]=min(f[i][j-1],f[i+mi[j-1]][j-1]); } int lcp(int x,int y) { if (x<1||y>n||x==y) return 0; x++;int z=lg[y-x+1]; return min(f[x][z],f[y-mi[z]+1][z]); } void prepare() { fo(i,1,n) suf[i]=i+1,pre[i]=i-1; suf[0]=1;pre[n+1]=n; } void ins(int x) { ans=max(ans,max(lcp(pre[x],x),lcp(x,suf[x]))); suf[pre[x]]=x;pre[suf[x]]=x; } void del(int x) { pre[suf[x]]=pre[x]; suf[pre[x]]=suf[x]; } int main() { freopen("history.in","r",stdin); freopen("history.out","w",stdout); scanf("%d%d",&n,&m); scanf("%s",st+1); fo(i,1,n/2) swap(st[i],st[n-i+1]); fo(i,1,n) x[i]=st[i]; get_sa(); get_height(); get_rmq(); fo(i,1,n) b[i]=(i-1)/M+1; fo(i,1,m) { scanf("%d%d",&l,&r); swap(l,r);l=n-l+1,r=n-r+1; if (b[l]==b[r]) { fo(j,l,r) a[j-l+1]=rank[j]; sort(a+1,a+r-l+1+1); ans=0; fo(j,2,r-l+1) ans=max(ans,lcp(a[j-1],a[j])); an[i]=ans; continue; } ask[++tot].id=i; ask[tot].l=l;ask[tot].r=r; } sort(ask+1,ask+tot+1); int cnt=(n-1)/M+((n-1)%M>0); fo(i,1,cnt) L[i]=R[i-1]+1,R[i]=min(L[i]+M-1,n); prepare();L[cnt+1]=n+1; int l=1,r=n; fo(i,1,tot) { if (b[ask[i].l]!=b[ask[i-1].l]) { prepare();r=n; while (L[l+1]<=ask[i].l) l++; fo(j,1,R[l]-1) del(rank[j]); fd(j,n,R[l]+1) del(rank[j]); ans=0; fo(j,R[l]+1,n) ins(rank[j]),a[j]=ans; fd(j,R[l]-1,L[l]) ins(rank[j]); } while (r>ask[i].r) del(rank[r]),r--; ans=a[r]; fo(j,L[l],R[l]-1) del(rank[j]); fd(j,R[l]-1,ask[i].l) ins(rank[j]); an[ask[i].id]=ans; fd(j,ask[i].l-1,L[l]) ins(rank[j]); } fo(i,1,m) printf("%d\n",an[i]); }
相关文章推荐
- [JZOJ5027]【NOI2017模拟3.25】历史行程
- 【NOI2017模拟3.25】跳蚤王国
- [JZOJ5029]. 【NOI2017模拟3.25】围墙
- 【JZOJ5037】【NOI2017模拟3.30】轮回
- 【JZOJ5037】【NOI2017模拟3.30】轮回
- 【NOI2017模拟4.5】机器人游戏【搜索,DP】
- 【NOI2017模拟4.4】保持平衡【优先队列,贪心】
- [JZOJ5044]【NOI2017模拟4.4】Sone0
- 【NOI2017模拟.4.1】Shoes【DP决策单调性,主席树,分治】
- 【NOI2017模拟4.2】查询
- 【NOIP2017提高组A组模拟8.17】行程的交集
- 【NOI2017模拟3.30】原谅
- Sevlet模拟显示商品的浏览历史
- 【JZOJ5045】【NOI2017模拟4.5】无限棋盘
- 【JZOJ5040】【NOI2017模拟4.2】押韵
- 【NOI2017模拟.4.1】 Tree【最大费用循环流】
- 【Weiss】【第03章】练习3.25:数组模拟队列
- 【NOI2017模拟4.4】保持平衡
- [JZOJ100019] 【NOI2017模拟6.26】A
- 微信公众号模拟登录,获取公众号历史链接