• 浅谈线段树分治


    线段树分治

    首先我们要理解线段树(现在指狭义的线段树)是什么。

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

    准确来说,比如你维护区间 [L,R],

    其实就可以不断以中点分治下去。

    由于每次分治区间长度都会除以 2 ,所以最多分治 log层,就形成了线段树。

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

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

    但是,维护时间区间的东西还有很多,

    比如 CDQ 分治,KD-Tree ,这一类数据结构都能维护时间区间

    那线段树分治的特殊作用在哪里呢?

    实际上,一般而言,它的作用就是支持撤销操作,或者说就是维护了一个操作影响的时间区间.

    例题:

    题目大意
    有一幅图,n个点,m条边,边有边权。

    有三种操作:加边,删边,询问把图划分为两个点 集后两个端点属于同一个点集的边的最大值的最小值。

    solution:

    每次询问将当前存在的边排序后,从大到小依次加入,若加入一条边后出现奇环,则这条边就是答案

    可以发现,那些加进去只产生偶环的边是没有用的

    因此只 有O(n)条边是有用的(即不产生环的边和第一次产生奇环的边)。

    考虑线段树分治,把每条边按其存在时间的区间加入到线段树中。

    对线段树每个节点,会 有许多条边。

    预处理出每个节点上有用的O(n)条边。

    然后每次询问就是把O(logn)个节点上的 信息合并。

    令询问次数为Q。

    由于可以将询问看作时间点建线段树,那么线段树中只有O(Q)个节点。

    每个节点的信息可以由其父亲得来

    #include<bits/stdc++.h>
    using namespace std;
    const int MAX_N=5+1e3;
    int n;
    struct DSF{
    	int fa[MAX_N],p[MAX_N];
    	void make_set(int x){ fa[x]=x,p[x]=0; }
    	int find_set(int x){ //并查集维护奇偶性
    		if(fa[x]!=x){
    			int y=fa[x];
    			fa[x]=find_set(fa[x]);
    			p[x]^=p[y];
    		}
    		return fa[x];
    	}
    	bool merge(int x,int y){
    		if(find_set(x)==find_set(y))
    			return p[x]^p[y];
    		int u=find_set(x),v=find_set(y);
    		p[u]=p[x]^p[y]^1;
    		fa[u]=v;
    		return true;
    	}
    	bool member(int x,int y){
    		return find_set(x)==find_set(y);
    	}
    }dsf;
    struct E{ int x,y,k; };
    struct Q{ E x; int l,r; };
    inline bool operator<(E a,E b){ return a.k>b.k; }
    vector<Q> e;
    struct SEG{
    	vector<E> tree[MAX_N<<2];
    	void build(int p,int l,int r){
    		tree[p].clear(); 
    		if(l==r) return;
    		int mid=l+r>>1;
    		build(p+p,l,mid);
    		build(p+p+1,mid+1,r);
    	}
    	void change(int p,int l,int r,int x,int y,E key){
    		if(l==x&&r==y){
    			tree[p].push_back(key);
    			return;
    		}
    		int mid=l+r>>1;
    		if(y<=mid) return change(p+p,l,mid,x,y,key);
    		else if(x>mid) return change(p+p+1,mid+1,r,x,y,key);
    		else change(p+p,l,mid,x,mid,key),change(p+p+1,mid+1,r,mid+1,y,key);
    	}
    	void dfs(int p,int l,int r,vector<E> k,int ans){	
    		for(int i=0;i<tree[p].size();++i)
    			k.push_back(tree[p][i]);
    		sort(k.begin(),k.end());
    		for(int i=1;i<=n;++i) dsf.make_set(i);
    		for(int i=0;i<k.size();++i){ 
    			if(!dsf.member(k[i].x,k[i].y)){
    				dsf.merge(k[i].x,k[i].y);
    			}else{
    				if(!dsf.merge(k[i].x,k[i].y)){
    					ans=max(ans,k[i].k);
    					k.resize(i);//只保留[0,i-1] 
    					 break;
    				}
    			}
    		}
    		if(l==r){
    			printf("%d
    ",ans);
    			return;
    		}
    		int mid=l+r>>1;
    		dfs(p+p,l,mid,k,ans);
    		dfs(p+p+1,mid+1,r,k,ans);
    	}
    }seg;
    int main(){
    	int m,q; scanf("%d%d%d",&n,&m,&q);
    	e.resize(m+1); 
    	for(int i=1;i<=m;++i){ 
    		scanf("%d%d%d",&e[i].x.x,&e[i].x.y,&e[i].x.k);
    		e[i].l=1; e[i].r=-1;
    	}
    	vector<E> k; for(int i=1;i<=m;++i) k.push_back(e[i].x);
    	sort(k.begin(),k.end());
    	for(int i=1;i<=n;++i) dsf.make_set(i);
    	int top=1;
    	for(int i=1;i<=q;++i){
    		char c=getchar();
    		while(c<'A'||c>'Z') c=getchar();
    		if(c=='D'){
    			int x; scanf("%d",&x);
    			e[x].r=top-1;
    		}else if(c=='A'){
    			e.push_back((Q){{0,0,0},0,0}); ++m;
    			scanf("%d%d%d",&e[m].x.x,&e[m].x.y,&e[m].x.k);
    			e[m].l=top; e[m].r=-1;
    		}else{
    			++top;
    		}
    	}
    	for(int i=1;i<=m;++i)
    		if(e[i].r==-1) e[i].r=top-1;
    	--top;
    	seg.build(1,1,top);
    	for(int i=1;i<=m;++i)
    		if(e[i].l<=e[i].r)
    			seg.change(1,1,top,e[i].l,e[i].r,e[i].x);
    	seg.dfs(1,1,top,vector<E>(),0);
    	return 0;
    }
    
  • 相关阅读:
    ZOJ 3769 Diablo III(分组背包)
    HDU 1712 ACboy needs your help(分组背包入门题)
    POJ 1170 Shopping Offers(完全背包+哈希)
    HDU 4489 The King’s Ups and Downs
    [转] LINUX 三种网络连接模式
    [转] 软件架构
    [转] 支付宝系统架构内部剖析
    [转] pip镜像升级报警 -trust-host问题解决方案
    [转] Linux 查找文件内容
    [转] CentOS系统目录学习
  • 原文地址:https://www.cnblogs.com/wzxbeliever/p/11604715.html
Copyright © 2020-2023  润新知