zoukankan      html  css  js  c++  java
  • 二进制分组学习小记:

    二进制分组学习小记:

    例题:

    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");
    		}
    	}
    }
    
    
  • 相关阅读:
    11
    消除左递归
    4.K均值算法--应用
    3.K均值算法
    2.机器学习相关数学基础作业
    机器算法学习第一次作业
    第十五次作业
    第十四次作业
    第十三次作业
    第十二次作业
  • 原文地址:https://www.cnblogs.com/coldchair/p/12575650.html
Copyright © 2011-2022 走看看