BZOJ 4556 [Tjoi2016&Heoi2016]字符串
2017-02-10 20:34
323 查看
后缀数组+可持久化线段树+二分
啊啊啊智商好低,想了好久。
一个直观的想法是在s[a…b]中找到和s[c…d]最接近的串,使得height最大。然而一个很烦的事情是s[a…b]存在一个右边界b,意味着我们需要min一个边界值。遇到这种min啊max啊的东西一般考虑强行分类讨论。
于是二分一个答案mid,那能贡献mid答案的串开头一定位于s[a…b-mid+1]之中,那边界的条件就没了,这样就可以直接用height了。这东西可持久化线段树套一下即可。
啊啊啊智商好低,想了好久。
一个直观的想法是在s[a…b]中找到和s[c…d]最接近的串,使得height最大。然而一个很烦的事情是s[a…b]存在一个右边界b,意味着我们需要min一个边界值。遇到这种min啊max啊的东西一般考虑强行分类讨论。
于是二分一个答案mid,那能贡献mid答案的串开头一定位于s[a…b-mid+1]之中,那边界的条件就没了,这样就可以直接用height了。这东西可持久化线段树套一下即可。
#include<cstdio> #include<iostream> #include<algorithm> #define N 200005 #define cmin(u,v) (u)>(v)?(u)=(v):0 #define cmax(u,v) (u)<(v)?(u)=(v):0 using namespace std; namespace runzhe2000 { const int INF = 1<<30; char s ; int n, m, t1 , t2 , sa , rank , sum , height , lef, rig; void SA_build() { int *x = t1, *y = t2, m = 30; for(int i = 1; i <= n; i++) sum[x[i] = s[i] - 'a' + 1]++; for(int i = 1; i <= m; i++) sum[i] += sum[i-1]; for(int i = n; i >= 1; i--) sa[sum[x[i]]--] = i; for(int k = 1; k <= n; k <<= 1) { int p = 0; for(int i = n-k+1; i <= n; i++) y[++p] = i; for(int i = 1; i <= n; i++) if(sa[i] - k > 0) y[++p] = sa[i] - k; for(int i = 1; i <= m; i++) sum[i] = 0; for(int i = 1; i <= n; i++) sum[x[i]]++; for(int i = 1; i <= m; i++) sum[i] += sum[i-1]; for(int i = n; i >= 1; i--) sa[sum[x[y[i]]]--] = y[i]; swap(x, y); for(int i = 1; i <= n; i++) x[sa[i]] = x[sa[i-1]] + (y[sa[i]] == y[sa[i-1]] && y[sa[i]+k] == y[sa[i-1]+k] ? 0 : 1); m = x[sa ]; if(m == n) break; } for(int i = 1; i <= n; i++) rank[sa[i]] = i; for(int i = 1, k = 0; i <= n; height[rank[i++]] = k?k--:k) for(; s[i+k] == s[sa[rank[i]-1]+k]; k++); } struct seg { int mi, cnt; seg *ch[2]; void pushup() { mi = min(ch[0]->mi, ch[1]->mi); cnt = ch[0]->cnt + ch[1]->cnt; } }mem[N*15], *tot, *null, *root ; seg* seg_new() { seg *p = ++tot; *p = *null; return p; } void seg_init() { null = tot = mem; null->mi = INF, null->cnt = 0; null->ch[0] = null->ch[1] = null; for(int i = 0; i <= n; i++) root[i] = seg_new(); } void seg_make(seg *x, int l, int r) { if(l == r){x -> mi = height[l]; return;} int mid = (l+r)>>1; x->ch[0] = seg_new(); x->ch[1] = seg_new(); seg_make(x->ch[0], l, mid); seg_make(x->ch[1], mid+1, r); x->pushup(); } void seg_ins(seg *x, seg *y, int l, int r, int pos) { *y = *x; if(l == r){y->cnt++; return;}; int mid = (l+r)>>1; if(pos <= mid) { y->ch[0] = seg_new(); y->ch[1] = x->ch[1]; seg_ins(x->ch[0], y->ch[0], l, mid, pos); } else { y->ch[0] = x->ch[0]; y->ch[1] = seg_new(); seg_ins(x->ch[1], y->ch[1], mid+1, r, pos); } y->pushup(); } bool find(seg *x, seg *y, int l, int r, int ql, int qr, int isright) { if(ql > qr) return 0; if(ql <= l && r <= qr) { if(y->cnt - x->cnt == 0) return 0; else if(l == r) { if(!isright) cmax(lef, l); else cmin(rig, l); return 1; } } int mid = (l+r)>>1; if(!isright) // left max { if(mid < qr) {if(find(x->ch[1], y->ch[1], mid+1, r, ql, qr, isright)) return 1;} if(ql <= mid) return find(x->ch[0], y->ch[0], l, mid, ql, qr, isright); } else // right min { if(ql <= mid) {if(find(x->ch[0], y->ch[0], l, mid, ql, qr, isright)) return 1;} if(mid < qr) return find(x->ch[1], y->ch[1], mid+1, r, ql, qr, isright); } } int query(seg *x, int l, int r, int ql, int qr) { if(ql > qr) return INF; if(ql <= l && r <= qr)return x->mi; int mid = (l+r)>>1, p1 = INF, p2 = INF; if(ql <= mid) p1 = query(x->ch[0], l, mid, ql, qr); if(mid < qr) p2 = query(x->ch[1], mid+1, r, ql, qr); return min(p1, p2); } void main() { int happy = scanf("%d%d%s",&n,&m,s+1); s[++n] = 'z' + 1; SA_build(); seg_init(); seg_make(root[0],1,n); for(int i = 1; i <= n; i++) seg_ins(root[i-1], root[i], 1, n, rank[i]); for(int i = 1; i <= m; i++) { int a, b, c, d, p, l, r; happy = scanf("%d%d%d%d",&a,&b,&c,&d); l = 0, r = min(d-c+1, b-a+1); for(; l < r; ) { int mid = (l+r+1)>>1, ans; lef = 0; rig = n; find(root[a-1], root[b-mid+1], 1, n, 1, rank[c], 0); find(root[a-1], root[b-mid+1], 1, n, rank[c], n, 1); ans = max(query(root[0], 1, n, lef+1, rank[c]), query(root[0], 1, n, rank[c]+1, rig)); cmin(ans, mid); if(ans < mid) r = mid - 1; else l = mid; } printf("%d\n",l); } } } int main() { runzhe2000::main(); }
相关文章推荐
- [BZOJ4556][Tjoi2016&Heoi2016]字符串(后缀数组+二分+st表+主席树)
- [达成成就:Tjoi2016&Heoi2016全AC] bzoj 4556: [Tjoi2016&Heoi2016]字符串 后缀数组+可持久化线段树
- [BZOJ4556][Tjoi2016&Heoi2016]字符串 后缀数组+主席树
- [BZOJ4556][Tjoi2016&Heoi2016]字符串 主席树+二分+倍增+后缀自动机
- bzoj4556: [Tjoi2016&Heoi2016]字符串 (后缀数组加主席树)
- bzoj 4556: [Tjoi2016&Heoi2016]字符串
- BZOJ4556 [Tjoi2016&Heoi2016]字符串
- BZOJ 4556: [Tjoi2016&Heoi2016]字符串 后缀数组 主席树
- Bzoj4556: [Tjoi2016&Heoi2016]字符串
- bzoj 4556: [Tjoi2016&Heoi2016]字符串 (主席树+二分+后缀数组+ST表||后缀自动机+线段树合并+LCA)
- bzoj 4556 [Tjoi2016&Heoi2016]字符串
- BZOJ 4556 [Tjoi2016&Heoi2016]字符串 ——后缀数组 ST表 主席树 二分答案
- 【BZOJ4556】[Tjoi2016&Heoi2016]字符串 后缀数组+二分+主席树+RMQ
- bzoj 4556 [Tjoi2016&Heoi2016]字符串 二分+后缀数组+主席树+RMQ
- BZOJ 4556: [Tjoi2016&Heoi2016]字符串
- bzoj 4556: [Tjoi2016&Heoi2016]字符串
- [BZOJ4556][TJOI2016&&HEOI2016]字符串(二分答案+后缀数组+RMQ+主席树)
- 【BZOJ 4556】[Tjoi2016&Heoi2016]字符串 SAM+二分+主席树
- ●BZOJ 4556 [Tjoi2016&Heoi2016]字符串
- [bzoj4556][Tjoi2016&Heoi2016]字符串