最后冲刺!!!|

铃狐sama

园龄:1年11个月粉丝:5关注:5

线段树分治结构

线段树分治结构

基本知识:

应用点: 当有些东西一会出现,一会又不出现时,可以使用线段树分治
方式: 维护某一个东西出现的时间段,并在线段树上打上标记,并dfs
遇到标记后,就相当于加入了这个物品。当dfs到叶子节点后,就可以得到叶子节点所代表的时间的性质
dfs返回时,若经过遇到标记的地方,需要撤回这个标记,相当于这个物品不再在答案统计内
正因如此,若是要用某个结构来维护性质,要求能够撤回
这些常见的结构有:线性运算/并查集(例题1)/dp方程式(例题2)

例题1: 模板(基础题)

题目链接:https://www.luogu.com.cn/problem/P5787
AC记录:https://www.luogu.com.cn/record/116043014
思想: 并查集维护同色关系,可以拆点或带权并查集,利用栈来维护撤回操作
代码:


#include<bits/stdc++.h>
using namespace std;
int n,m,k;
struct node{
	int l,r,x,y;
}line[200005];
struct nod{
	int ll;
	int rr;
}tree[800045];
int d[200005];
stack<pair<int,bool>>s;
vector<pair<int,int>>flag[800045];
void build(int rt,int l,int r){
	tree[rt].ll=l;
	tree[rt].rr=r;
	if(l==r){
		return;
	}
	int mid=(l+r)/2;
	build(rt*2,l,mid);
	build(rt*2+1,mid+1,r);
}
void updata(int rt,int cl,int cr,int flag1,int flag2){
	int le=tree[rt].ll;
	int ri=tree[rt].rr;
	if(le>cr||ri<cl){
		return;
	}
	if(le>=cl&&ri<=cr){
		flag[rt].push_back(make_pair(flag1,flag2));
		return;
	}
	updata(rt*2,cl,cr,flag1,flag2);
	updata(rt*2+1,cl,cr,flag1,flag2);
}
//-----------------------------------------打标记线段树 
int fa[400005];
int fid(int x){
	if(x==fa[x]){
		return x;
	}
	else{
		return fid(fa[x]);
	}
}
void he(int x, int y){
	x=fid(x);
	y=fid(y);
	if(x==y){
		return;	
	} 
	if(d[x]>d[y]) swap(x, y);
	s.push(make_pair(x,d[x]==d[y]));
	fa[x]=y;
	if(d[x]==d[y]){
		d[y]++;
	}
}

void dfs(int rt,int right){
	int l=tree[rt].ll;
	int r=tree[rt].rr;
	int o=s.size();
	for(int i=0;i<flag[rt].size();i++){
		int a=flag[rt][i].first;
		int b=flag[rt][i].second;
		if(fid(a)==fid(b)){
			for(int j=l;j<=r;j++){
				cout<<"No"<<endl;
				
			}
			right=0;
			break;
		}
		else{
			he(a,b+n);
			he(b,a+n);
		}
		
	}//标记使用
	if (right) {
		if (l==r){
			cout<<"Yes"<<endl;
		}
		else{
			dfs(rt*2,right); 
			dfs(rt*2+1,right);
		} 
	}
	while (s.size() > o){
		d[fa[s.top().first]]-=s.top().second;
		fa[s.top().first]=s.top().first;
		s.pop();
	} 
}
int main(){
	ios::sync_with_stdio(false);
	cin >> n >> m >> k;
	build(1,0,k-1);
	for(int i=1;i<=m;i++){
		cin >> line[i].x>>line[i].y>>line[i].l>>line[i].r;
		line[i].r--;
		updata(1,line[i].l,line[i].r,line[i].x,line[i].y);
	}
	for(int i=1;i<=2*n;i++){
		fa[i]=i;
		d[i]=1;
	}
	dfs(1,1);
	
	
}

example 2

https://www.luogu.com.cn/problem/P5227


#include<bits/stdc++.h>
using namespace std;
struct li{
	int l;
	int r;
	int id;
}line[200005];
int n,m;
//考虑从后到前加边是錯的
/*
md把这道题想难了,我以为是每一次操作有后续性 
注意下去重问题!!!!!!!!!并不是说一个边只会删除一次,而剩下时间都是存在的!

注意下inc必需寫成局部變量!!! 
*/
bool vis[400005] ;
vector<int>del[400005];
vector<int>sign[1600005];

struct tr{
	int ll;
	int rr;
}tree[1600005];
void build(int rt,int l,int r){
	int mid=(l+r)>>1;
	tree[rt].ll=l;
	tree[rt].rr=r;
	if(l==r){
		return ;
	}
	build(rt*2,l,mid);
	build(rt*2+1,mid+1,r);
}
void updata(int rt,int cl,int cr,int id){
	int l=tree[rt].ll;
	int r=tree[rt].rr;
//	int mid=(l+r)/2;
	if(l>cr||r<cl){
		return;
	}
	if(cl<=l&&r<=cr){
		sign[rt].push_back(id);
		return;
	}
	updata(rt*2,cl,cr,id);
	updata(rt*2+1,cl,cr,id);
}
bool ans[400005];
//------------------------------------
int fa[400005];
int sz[400005];
int fid(int x){
	if(fa[x]==x){
		return x;
	}
	else return fid(fa[x]);
}
struct nod{
	int x;
	int y;
	int szx;
	int szy;
};
stack<nod>st;
void he(int x,int y){
	x=fid(x);
	y=fid(y);
	if(x==y){
		return ;
	}
	st.push((nod){x,y,sz[x],sz[y]});
	if(sz[x]<sz[y]){
		fa[x]=y;
		sz[y]+=sz[x];
	}
	else{
		fa[y]=x;
		sz[x]+=sz[y];
	}
}

//------------------------------------
void dfs(int rt){
	int	inc=st.size();
	for(int i=0;i<sign[rt].size();i++){
		int now=sign[rt][i];
		he(line[now].l,line[now].r);
	}
	if(tree[rt].ll==tree[rt].rr){
		if(sz[fid(n)]==n){
			ans[tree[rt].ll]=1;
		}
		else{
			ans[tree[rt].ll]=0;
		}
		
		while(st.size()>inc){
			nod t=st.top();
			st.pop();
			fa[t.x]=t.x;
			fa[t.y]=t.y;
			sz[t.x]=t.szx;
			sz[t.y]=t.szy;
		}
		return;
	}
	dfs(rt*2);
	dfs(rt*2+1);
	while(st.size()>inc){
		nod t=st.top();
		st.pop();
		fa[t.x]=t.x;
		fa[t.y]=t.y;
		sz[t.x]=t.szx;
		sz[t.y]=t.szy;
	}
}
int main(){
	ios::sync_with_stdio(false);
	cin >> n >> m;
	for(int i=1;i<=m;i++){
		cin >> line[i].l >> line[i].r;
		line[i].id=i;
	}
	for(int i=1;i<=n;i++){
		fa[i]=i;
		sz[i]=1;
	}
	int k;
	cin >> k;
	build(1,1,k); 
	for(int i=1;i<=k;i++){
		int szz;
		cin >> szz;
		for(int j=1;j<=szz;j++){
			int a;
			cin >> a;
//			del[i].push_back(a);//i+1时加入了a这些边 ,如0时刻删除的一组在1时刻还存在 
			del[a].push_back(i);//某个边不能存在的时间 
			vis[a]=1;
		}
	}
	for(int i=1;i<=m;i++){
		if(vis[i]==0){//从未删除 
			updata(1,1,k,i); 
		}
		else{
			sort(del[i].begin(),del[i].end());
			int lasforbid=0;
			for(int j=0;j<del[i].size();j++){
				int forbid=del[i][j];
				if(lasforbid+1>forbid-1){//在2,3都被禁止时,到3时会满足此条件 
					lasforbid=forbid;
					continue;
				}
				updata(1,lasforbid+1,forbid-1,i);
				lasforbid=forbid;
			}
			if(lasforbid<k){
				updata(1,lasforbid+1,k,i);
			}
		} 
	}
	dfs(1);
	for(int i=1;i<=k;i++){
		if(ans[i]==1){
			cout<<"Connected"<<endl;
		}
		else{
			cout<<"Disconnected"<<endl;
		}
	}
}
 

例题3: dp(背包)(会了就掌握题)

题目链接:http://222.180.160.110:1024/problem/5503
也可以去:https://loj.ac/p/6515
思想: 物品我一个个加入,同理我也可以一个个撤回,其实还是枚举了物品的,不过是有规律的罢了,利用cnt来记录物品进入先后关系(从而好撤回)
代码:有点难写啊

#include<bits/stdc++.h>
using namespace std;
int t,m,mod;
const int M=50005;
int tmm[M];
int L[M],R[M],ans[M];
struct node{
	int tim;
	int w;
	int v;
};
//----------------------pack
int dp[M][505]; 
int cnt=0;
void clear(){
	for(int i=0;i<M;i++){
		for(int j=0;j<500;j++){
			dp[i][j]=-0x3f3f3f3f;
		}
	}
	dp[0][0]=0;
}
void insert(pair<int,int>now){
	int x=now.first%mod;
	int y=now.second;
	cnt++;
	for(int i=0;i<mod;i++){
		dp[cnt][i]=dp[cnt-1][i];
	}
	for(int i=0;i<mod;i++){
		if(x<=i){
			dp[cnt][i]=max(dp[cnt][i],dp[cnt-1][i-x]+y);
		}
		else{
			dp[cnt][i]=max(dp[cnt][i],dp[cnt-1][i+mod-x]+y);
		}
	}
}
void recall(){
	for(int i=0;i<500;i++){
		dp[cnt][i]=-0x3f3f3f3f;
	}
	cnt--;
}
int query(int l,int r){
	int ret=-1;
	for(int i=l;i<=r;i++){
		ret=max(ret,dp[cnt][i]);
	}
	return ret;
}

//---------------------tree
struct nod{
	int ll;
	int rr;
}tree[4*M];
vector<pair<int,int>>flag[4*M];
void build(int rt,int l,int r){
	tree[rt].ll=l;
	tree[rt].rr=r;
	if(l==r){
		return;
	}
	int mid=(l+r)/2;
	build(rt*2,l,mid);
	build(rt*2+1,mid+1,r);
}
void updata(int rt,int cl,int cr,int weight,int value){
	int le=tree[rt].ll;
	int ri=tree[rt].rr;
	if(le>cr||ri<cl){
		return;
	}
	if(le>=cl&&ri<=cr){
		flag[rt].push_back(make_pair(weight,value));
		return;
	}
	updata(rt*2,cl,cr,weight,value);
	updata(rt*2+1,cl,cr,weight,value);
}
void solve(int rt){
	int l=tree[rt].ll;
	int r=tree[rt].rr;
	int sz=flag[rt].size();
	for(int i=0;i<sz;i++){
		insert(flag[rt][i]);
	}
	if(l^r){
		solve(rt*2);
		solve(rt*2+1);
		
	}
	else if(L[l]^-1){
		ans[l]=query(L[l],R[r]);
	}
	
	for(int i=0;i<sz;i++){
		recall();
	}
}
//---------------------------tree
int main(){
		ios::sync_with_stdio(false);
		cin >> t;
		cin >> m >> mod;
		clear();
		memset(L,-1,sizeof(L));
		build(1,1,m); 
		deque<node>q;
		for(int i=1;i<=m;i++){
			string op;
			cin >> op;
			int w,v;
			if(op[0]=='I'&&op[1]=='F'){
				cin >> w >> v;//前面放 
				node Q=(node){i,w,v};
				q.push_front(Q);
			}
			else if(op[0]=='I'&&op[1]=='G'){
				cin >> w >> v;//后面放 
				node Q=(node){i,w,v};
				q.push_back(Q);
			}
			else if(op[0]=='D'&&op[1]=='F'){//删除前面 
				node Q=q.front();
				q.pop_front();
				updata(1,Q.tim,i-1,Q.w,Q.v);
			}
			else if(op[0]=='D'&&op[1]=='G'){//删除后面 
				node Q=q.back();
				q.pop_back();
				updata(1,Q.tim,i-1,Q.w,Q.v);//区间标记 
			}
			else if(op[0]=='Q'&&op[1]=='U'){
				int l,r;
				cin >> l >> r;//w和mod在l-r区间内最强武力值 
				L[i]=l;
				R[i]=r;
			}
		}
		while(q.size()){
			node Q=q.front();
			q.pop_front();
			updata(1,Q.tim,m,Q.w,Q.v);//永远不会消失的要注意添加 
		}
		solve(1);
		for(int i=1;i<=m;i++){
			if(L[i]^-1){
				cout<<ans[i]<<endl;
			}
		}
	
}

本文作者:linghusama

本文链接:https://www.cnblogs.com/linghusama/p/17560657.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   铃狐sama  阅读(13)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起