二进制分组学习小记:

二进制分组学习小记:

例题:

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;
		}
	}
}

其它题:

https://loj.ac/problem/3273

分析:

首先得会没有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");
		}
	}
}

原文地址:https://www.cnblogs.com/coldchair/p/12575650.html