• 线段树分治详解


    (预计有很多博客要补,之前算法的复杂度分析也想补一补,有一场模拟赛要订正,还有KDTree要学)

    线段树是一种容易维护区间的数据结构,是一种区间分治实体化的产物。

    准确来说,比如你维护区间[L,R],其实就可以不断以中点分治下去。由于每次分治区间长度都会除以2,所以最多分治log层,就形成了线段树。

    那么线段树分治指什么呢?

    实际上是一种维护时间区间的数据结构,同样是利用线段树的分治性,让复杂度保证在了log级别。

    但是,维护时间区间的东西还有很多,比如CDQ分治,比如KD-Tree,这一类数据结构都能维护时间区间,那线段树分治的特殊作用在哪里呢?实际上,一般而言,它的作用就是支持撤销操作,或者说就是维护了一个操作影响的时间区间,这是CDQ分治等不容易做到的,是它这种结构的特殊优势所在。

    几道例题来说明一下吧(可能例题涉及到了别的东西,又挖了几个坑)

    1、bzoj 向量

    前置知识:向量运算,凸包,三分

    插入一个向量再删除一个向量,那么该向量的有效区间就是插入的时间点-删除的时间点,或者没删除一直到最后,线段树分治维护

    剩下的问题就是如何快速找区间中有效点中与询问点点积最大的那一个

    一个结论:(口述好像也不太好说)

    一条与原点和查询点连线垂直的直线,从右上往左下或从右下往左上平移,遇到的第一个点,就是所求点,不难发现可以用凸包维护

    如果纵坐标>0时,维护一个上凸包,在凸包上三分最大点积,如果纵坐标<0就要维护一个下凸包,并在凸包上三分

    那么现在的问题就在于怎样维护一个凸包

    叉积:(a(x_1,y_1) imes b(x_2,y_2) = x_1y_2-x_2y_1)

    性质:

    1. (|a imes b|)的值就为以a,b为边构成平行四边形的面积

    2. (a imes b > 0)证明b在a的逆时针方向

    3. (a imes b < 0)证明b在a的顺时针方向

    	sort(vec[x].begin(),vec[x].end());
    	top = 0;
    	for (int i = 0;i < vec[x].size();i++){
    		node u = vec[x][i];
    		while (top > 1&&((u-st[top])^(st[top]-st[top-1])) <= 0) top--;
    		st[++top] = u;
    	}
    

    重载了运算符,先按x坐标排序,发现新加入的点形成的直线,在原来直线的逆时针方向就需要弹出原来直线的点(大致是这个意思吧)就跟斜率优化使用单调队列维护凸包一样

    剩下的就没什么问题了,我个人认为我的代码写的还是蛮明了的,看代码应该都能看明白吧

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    #include <vector>
    #define int long long
    using namespace std;
    int read(){
    	int x = 1,a = 0;char ch = getchar();
    	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
    	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
    	return x*a;
    }
    const int maxn = 1e6+10;
    struct node{
    	int x,y,tim;
    }a[maxn],q[maxn],st[maxn];
    int n,tot;
    bool operator < (node a,node b){return (a.x == b.x) ? (a.y < b.y) : (a.x < b.x);}
    int operator ^ (node a,node b){return a.x*b.y-a.y*b.x;}
    int operator * (node a,node b){return a.x*b.x+a.y*b.y;}
    node operator + (node a,node b){return node{a.x+b.x,a.y+b.y};}
    node operator - (node a,node b){return node{a.x-b.x,a.y-b.y};}
    int ls(int x){return x << 1;}
    int rs(int x){return x << 1 | 1;}
    vector<node> vec[maxn];
    void modify(int x,int l,int r,int nl,int nr,node k){
    	if (nl <= l&&r <= nr){vec[x].push_back(k);return;}
    	int mid = (l+r >> 1);
    	if (nl <= mid) modify(ls(x),l,mid,nl,nr,k);
    	if (nr > mid) modify(rs(x),mid+1,r,nl,nr,k); 
    }
    int top,ans[maxn];
    void query(int x){
    	node u = q[x];
    	int l = 1,r = top;
    	while (r-l >= 3){
    		int mid1 = (l+r >> 1),mid2 = (r+mid1 >> 1);
    		if (u*st[mid1] <= u*st[mid2]) l = mid1;
    		else r = mid2;
    	}
    	//cout<<l<<" "<<r<<endl;
    	for (int i = l;i <= r;i++) ans[x] = max(ans[x],u*st[i]);
    }
    void getans(int x,int l,int r){
    	sort(vec[x].begin(),vec[x].end());
    	top = 0;
    	for (int i = 0;i < vec[x].size();i++){
    		node u = vec[x][i];
    		while (top > 1&&((u-st[top])^(st[top]-st[top-1])) <= 0) top--;
    		st[++top] = u;
    	}
    	int mid = (l+r >> 1);
    	for (int i = l;i <= r;i++) if (q[i].x) query(i);
    	if (l == r) return;
    	getans(ls(x),l,mid),getans(rs(x),mid+1,r);
    }
    signed main(){
    //	freopen("in.in","r",stdin);
    //	freopen("out.out","w",stdout);
    	n = read();
    	for (int i = 1;i <= n;i++){
    		int op = read();
    		if (op == 1) a[++tot].x = read(),a[tot].y = read(),a[tot].tim = i;
    		if (op == 2){
    			int x = read();
    			modify(1,1,n,a[x].tim,i,a[x]),a[x].tim = 0;
    		}	
    		if (op == 3) q[i].x = read(),q[i].y = read();
    	}
    	for (int i = 1;i <= tot;i++) if (a[i].tim) modify(1,1,n,a[i].tim,n,a[i]);
    	getans(1,1,n);
    	for (int i = 1;i <= n;i++) if (q[i].x) printf("%lld
    ",ans[i]);
    	return 0;
    }
    

    二、八纵八横

    一个华点在于,一张图存在这环或者链,如果我想回头,链上的点要不不走,要不就需要走两次,贡献都为0,环上的点,都走相同的次数,偶数贡献为0,奇数的贡献就是环上边的异或和。

    这样问题就转化成求几个数的异或和最大是多少,不难想到用线性基维护

    这样我们就可以把一段时间内的所有环的异或存下来然后线性基求异或和最大值,由于它的二进制数有1000位,我们必须用bitset来打线性基

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    #include <vector>
    #include <bitset>
    using namespace std;
    int read(){
    	int x = 1,a = 0;char ch = getchar();
    	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
    	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
    	return x*a;
    }
    const int maxn = 6005;
    int n,m,p;
    int maxBS,tot;
    string str;
    struct node{
    	int u,v,l,r;
    	bitset<1005> w;
    }vec[maxn];
    struct edge{
    	int to,nxt;
    	bitset<1005> w;
    }ed[maxn];
    struct basis{
    	bitset<1005> bas[6005];
    }B;
    bitset<1005> dis[maxn];
    int fa[maxn],cnt;
    vector<int> T[maxn];
    void init(){for (int i = 1;i <= n;i++) fa[i] = i;}
    int find(int x){return (x == fa[x]) ? x : find(fa[x]);}
    int head[maxn],tot1;
    void add(int u,int to,bitset<1005> w){
    	ed[++tot1].to = to,ed[tot1].w = w,ed[tot1].nxt = head[u],head[u] = tot1;
    	ed[++tot1].to = u,ed[tot1].w = w,ed[tot1].nxt = head[to],head[to] = tot1;
    }
    void dfs(int x,int fa){
    	for (int i = head[x];i;i = ed[i].nxt){
    		int to = ed[i].to;
    		if (to == fa) continue;
    		dis[to] = dis[x]^ed[i].w;
    		dfs(to,x);
    	}
    }
    int pos[maxn];
    int ls(int x){return x << 1;}
    int rs(int x){return x << 1 | 1;}
    void insert(basis &x,bitset<1005> k){
    	for (int i = maxBS;i >= 0;i--){
    		if (!k[i]) continue;
    		if (!x.bas[i].any()){x.bas[i] = k;break;}
    		k ^= x.bas[i];
    	}
    }
    bitset<1005> query(basis x){
    	bitset<1005> res;res.reset();
    	for (int i = maxBS;i >= 0;i--) if (!res[i]) res ^= x.bas[i];
    	return res;
    }
    void print(bitset<1005> x){
    	int flag = 0;
    	for (int i = maxBS;i >= 0;i--){
    		if (x[i]) flag = 1;
    		if (flag) putchar(x[i]+'0');
    	}
    	if (!flag) putchar('0');
    	puts("");
    }
    void modify(int x,int l,int r,int nl,int nr,int k){
    	if (nl <= l&&r <= nr){T[x].push_back(k);return;}
    	int mid = (l+r >> 1);
    	if (nl <= mid) modify(ls(x),l,mid,nl,nr,k);
    	if (nr > mid) modify(rs(x),mid+1,r,nl,nr,k);
    }
    void getans(int x,int l,int r,basis bas){
    	int len = T[x].size();
    	//cout<<len<<endl;
    	for (int i = 0;i < T[x].size();i++) insert(bas,dis[vec[T[x][i]].u]^dis[vec[T[x][i]].v]^vec[T[x][i]].w);
    	int mid = (l+r >> 1);
    	if (l == r){print(query(bas));return;}
    	getans(ls(x),l,mid,bas),getans(rs(x),mid+1,r,bas);
    }
    int main(){
    	n = read(),m = read(),p = read();
    	init();
    	for (int i = 1;i <= m;i++){
    		int x = read(),y = read();cin >> str;
    		int len = str.length();
    		maxBS = max(maxBS,len);
    		bitset<1005> w(str);
    		if (find(x) != find(y)) fa[find(y)] = find(x),add(x,y,w);
    		else vec[++tot] = node{x,y,0,p,w};
    	}
    	dfs(1,0);
    	for (int i = 1;i <= p;i++){
    		char op[10];scanf ("%s",op);
    		if (op[0] == 'A'){
    			int x = read(),y = read();cin >> str;
    			int len = str.length();
    			maxBS = max(maxBS,len);
    			bitset<1005> w(str);
    			vec[++tot] = node{x,y,i,p,w},pos[++cnt] = tot;
    		}
    		if (op[0] == 'C'&&op[1] == 'a'){
    			int x = read();
    			vec[pos[x]].r = i-1,pos[x] = 0;
    		}
    		if (op[0] == 'C'&&op[1] == 'h'){
    			int x = read();cin >> str;
    			int len = str.length();
    			maxBS = max(maxBS,len);
    			bitset<1005> w(str);
    			vec[pos[x]].r = i-1;
    			vec[++tot] = node{vec[pos[x]].u,vec[pos[x]].v,i,p,w};
    			pos[x] = tot;
    		}
    	}
    	for (int i = 1;i <= tot;i++) modify(1,0,p,vec[i].l,vec[i].r,i);
    	getans(1,0,p,B);
    	return 0;
    }
    
  • 相关阅读:
    工作之余
    用MFC如何高效地绘图
    C++运算符优先级
    CentOS5.9下用Kate
    3G门户网(3G.cn) 招聘 软件测试工程师
    深圳市东润信息咨询有限公司招聘职位: 3G无线产品经理
    广州杰赛科技股份有限公司 招聘 技术中心3G协议软件工程师
    3G门户网(3G.cn) 招聘 手机游戏开发工程师
    3G门户网(3G.cn) 招聘 技术支持工程师
    3G工程师:三大热门的3G职业资格培训认证
  • 原文地址:https://www.cnblogs.com/little-uu/p/14860201.html
Copyright © 2020-2023  润新知