二进制分组学习小记:
例题:
https://codeforces.com/problemset/problem/710/F
分析:
删除相当于加入系数=-1的一个串。
如果离线的话,每个串存在时间是一个区间(后缀),给它分到线段树上去。
对于线段树上的一个区间,就可以bfs建这上面的所有串AC自动机,然后询问。
可惜这题强制在线。
考虑二进制分组,把现在有的串进行二进制分组,一个组里维护一个AC自动机。
例如n=7,就按二进制分为:4+2+1
当新加一个串时,先让它自成一个组,丢到最后面去。
比如n=7时,新加一个组就是4+2+1+1
发现最后两个组的大小相同,就合并它们,变成4+2+(1+1=2),注意对新的那个组进行AC自动机重构。
发现最后两个组大小仍然相同,继续合并,直到最后两个组大小不相同为止。
复杂度分析:
一个串所在的组每次被合并都会大小*2,所以一个串最多被合并log次。
一次询问也就询问当前的(log~n)个组。
那么复杂度就是(O(n~log~n))。
Code:
#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i < _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("
")
using namespace std;
#define fl fflush(stdout)
const int N = 6e5 + 5;
int T, op;
char s[N]; int len;
int cnt[N];
int son[N][26], tot;
int add(int x, int c) {
if(!son[x][c]) son[x][c] = ++ tot;
return son[x][c];
}
int d[N], d0, fa[N];
ll v[N];
void build(int rt) {
fo(j, 0, 25) son[0][j] = rt;
d[d0 = 1] = rt; fa[rt] = v[rt] = 0;
for(int i = 1; i <= d0; i ++) {
int x = d[i];
fo(j, 0, 25) if(son[x][j]) {
int y = son[x][j], k = fa[x];
while(!son[k][j]) k = fa[k];
k = son[k][j];
fa[y] = k;
v[y] = cnt[y] + v[fa[y]];
d[++ d0] = y;
}
}
}
int mer(int i, int j) {
if(!i || !j) return i + j;
cnt[i] += cnt[j];
fo(k, 0, 25) son[i][k] = mer(son[i][k], son[j][k]);
return i;
}
const int M = 35;
int rt[M], siz[M], r0;
void work() {
while(r0 > 1 && siz[r0] == siz[r0 - 1]) {
rt[r0 - 1] = mer(rt[r0 - 1], rt[r0]);
siz[r0 - 1] += siz[r0];
r0 --;
build(rt[r0]);
}
}
int go(int x, int c) {
while(!son[x][c]) x = fa[x];
return son[x][c];
}
int main() {
scanf("%d", &T);
fo(ii, 1, T) {
scanf("%d", &op);
scanf("%s", s + 1); len = strlen(s + 1);
if(op < 3) {
rt[++ r0] = ++ tot; siz[r0] = 1;
int x = rt[r0];
fo(i, 1, len) x = add(x, s[i] - 'a');
cnt[x] += op == 1 ? 1 : -1;
build(rt[r0]);
work();
} else {
ll ans = 0;
fo(i, 1, r0) {
fo(j, 0, 25) son[0][j] = rt[i];
int x = rt[i];
fo(j, 1, len) {
x = go(x, s[j] - 'a');
ans += v[x];
}
}
pp("%lld
", ans); fl;
}
}
}
其它题:
分析:
首先得会没有4操作的,注意到被移动过一次的点就满足单调。
所以维护两棵平衡树,一棵是已经移动过的点,每次操作在上面二分并区间赋值。
另一棵是没有被移动过的点,用于找每次操作将会被移动的点。
这题,如果离线的话就是每个询问相当于询问一个区间的操作,用线段树分治解决。
在线的话,考虑用二进制分组当前的点。
当合并两个组得到新的组后,这个新的组就认为所有点都没有并移动过。
操作时,对log个组都进行操作就好了。
http://www.lydsy.com/JudgeOnline/problem.php?id=4140
先考虑一个点在圆内的条件是什么:
设圆心是((u,v)),查询的点是((x,y))
则((x-u)^2+(y-v)^2<=u^2+v^2)
(2(ux+vy)>=x^2+y^2)
所以问题变为求(ux+vy)的最小值。
先讨论去掉(y=0)的情况。
式子变为(y(u{xover y}+v))
当(y>0),相当于求在一堆直线中的最大值。
当(y<0),相当于求在一堆直线中的最小值。
分别维护上凸壳和下凸壳,在上面二分即可。
如果可以离线,相当于每个圆存在于一个区间的时间,线段树分治淦过去即可。
这道加强版在线,那么用二进制分组,每次合并组之后重构即可。
时间复杂度:(O(n~log^2~n))
Code:
#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i < _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("
")
using namespace std;
#define db double
struct P {
db x, y;
P(db _x = 0, db _y = 0) {
x = _x, y = _y;
}
};
const db eps = 1e-8;
#define V vector<P>
#define pb push_back
#define si size()
#define re resize
const int N = 2e5 + 5;
int n, op, sum_yes;
db x, y;
const int M = 35;
V a[M], b[M], c[M];
int a0;
int cmpb(P a, P b) {
if(a.x == b.x) return a.y < b.y;
return a.x < b.x;
}
int cmpc(P a, P b) {
if(a.x == b.x) return a.y > b.y;
return a.x < b.x;
}
db jd(P a, P b) {
return (a.y - b.y) / (b.x - a.x);
}
void build(V &a, V &b, V &c) {
sort(a.begin(), a.end(), cmpb);
b.clear();
ff(j, 0, a.si) {
P w = a[j];
if(b.si && b[b.si - 1].x == w.x) continue;
while(b.si > 1 && jd(b[b.si - 1], b[b.si - 2]) <= jd(b[b.si - 1], w)) b.re(b.si - 1);
b.pb(w);
}
c.clear();
sort(a.begin(), a.end(), cmpc);
ff(j, 0, a.si) {
P w = a[j];
if(c.si && c[c.si - 1].x == w.x) continue;
while(c.si > 1 && jd(c[c.si - 1], c[c.si - 2]) >= jd(c[c.si - 1], w)) c.re(c.si - 1);
c.pb(w);
}
}
db solve(db x, db y, V &b, V &c) {
if(abs(y) < eps) return b[b.si - 1].x;
x /= y;
if(y > 0) {
int as = 0;
for(int l = 0, r = (int) b.si - 2; l <= r; ) {
int m = l + r >> 1;
db v = jd(b[m], b[m + 1]);
if(x <= v) as = m + 1, l = m + 1; else r = m - 1;
}
return y * (b[as].x * x + b[as].y);
} else {
int as = 0;
for(int l = 0, r = (int) c.si - 2; l <= r; ) {
int m = l + r >> 1;
db v = jd(c[m], c[m + 1]);
if(x >= v) as = m + 1, l = m + 1; else r = m - 1;
}
return y * (c[as].x * x + c[as].y);
}
}
int main() {
scanf("%d", &n);
fo(ii, 1, n) {
scanf("%d %lf %lf", &op, &x, &y);
x += sum_yes; y += sum_yes;
if(op == 0) {
a[++ a0].clear();
a[a0].pb(P(x, y));
build(a[a0], b[a0], c[a0]);
while(a0 > 1 && a[a0].si == a[a0 - 1].si) {
ff(i, 0, a[a0].si) a[a0 - 1].pb(a[a0][i]);
a[a0].clear();
a0 --;
build(a[a0], b[a0], c[a0]);
}
} else {
db mi = 1e18;
fo(i, 1, a0) {
mi = min(mi, solve(x, y, b[i], c[i]));
}
int ans = mi * 2 + eps >= x * x + y * y;
if(a0 == 0) ans = 0;
sum_yes += ans;
pp("%s
", ans ? "Yes" : "No");
}
}
}