首页 > 其他分享 ><学习笔记>线段树分治

<学习笔记>线段树分治

时间:2023-09-20 17:37:48浏览次数:32  
标签:int 线段 分治 tr top ans sum mod

一种离线处理方法

可以处理“具体哪个修改对询问有影响”、可以贡献不独立、可以支持插入删除。

例题

对这道题来说,对修改开线段树,线段树上每个节点开一个 \(vector\) 来维护出现在这段区间的线段,加入一个线段的区间,直接在区间查询时对所包含的节点压入这条线段就可以。

然后从根节点递归,先左子树再右子树,对途中经过的结点上的线段计算答案,这里用不压缩路径的并查集维护。对于加入,只需要将个数小的合并到个数多的(启发式合并),只需要将祖先的父亲和大小更改,所以开一个栈维护这个操作,对于删除就是将祖先更改过来。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
const int mod=1e9+7;
struct asd{
	int op,x,y;
}a[N],b[N];
struct tree{
	int l,r;
}tr[N*4];
struct qwe{
	int x,y,sizx,sizy;
}st[N];
int top=0;
vector<int> s[N*4];
map<int,int> mp[N],rk[N];
int cnt=0;
int mgml(int x,int p){
	int ans=1;
	while(p){
		if(p&1) ans=(ans*x)%mod;
		x=(x*x)%mod;
		p>>=1;
	}
	return ans;
}
void build(int p,int l,int r){
	tr[p].l=l,tr[p].r=r;
	if(l==r) return;
	int mid=(l+r)/2;
	build(p*2,l,mid);
	build(p*2+1,mid+1,r);
}
void change(int p,int l,int r,int v){
	if(tr[p].l>=l && tr[p].r<=r){
		s[p].push_back(v);
		return;
	}
	int mid=(tr[p].l+tr[p].r)/2;
	if(l<=mid) change(p*2,l,r,v);
	if(r>mid) change(p*2+1,l,r,v);
}
int fa[N];
int sum[N];
int get_fa(int x){
	if(fa[x]==x) return x;
	else return get_fa(fa[x]);
}
int ans=1;
int anss[N];
void add(int p){
	for(int i=0;i<s[p].size();i++){
		int id=s[p][i];
		int x=b[id].x,y=b[id].y;
		int fx=get_fa(x),fy=get_fa(y);
		if(fx==fy) continue;
		if(sum[fx]>sum[fy]) swap(fx,fy);
		st[++top]={fx,fy,sum[fx],sum[fy]};
		fa[fx]=fy;
		ans=ans*(sum[fx]+sum[fy])%mod*mgml(sum[fx]*sum[fy]%mod,mod-2)%mod;
		ans%=mod;
		sum[fy]+=sum[fx];
	}
}
void dele(int now,int p){
	while(top>now){
		int x=st[top].x,y=st[top].y;
		int sizx=st[top].sizx,sizy=st[top].sizy;
		ans=ans*sizx%mod*sizy%mod*mgml((sizx+sizy)%mod,mod-2)%mod;
		ans%=mod;
		fa[x]=x,fa[y]=y;
		sum[x]=sizx,sum[y]=sizy;
		top--;
	}
}
void dfs(int p){
	int now=top;
	add(p);
	if(tr[p].l==tr[p].r){
		anss[tr[p].l]=ans;
	}
	else{
		dfs(p*2),dfs(p*2+1);
	}
	dele(now,p);
} 
signed main(){
	int n,m;
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++){
		fa[i]=i;
		sum[i]=1;
	}
	build(1,1,m);
	for(int i=1;i<=m;i++){
		scanf("%lld%lld%lld",&a[i].op,&a[i].x,&a[i].y);
		if(rk[a[i].x][a[i].y]==0){
			rk[a[i].x][a[i].y]=rk[a[i].x][a[i].y]=++cnt;
			b[cnt].x=a[i].x,b[cnt].y=a[i].y;
		}
	}
	for(int i=1;i<=m;i++){
		if(a[i].op==1){
			mp[a[i].x][a[i].y]=mp[a[i].y][a[i].x]=i;
		}
		else{
			int st=mp[a[i].x][a[i].y];
			mp[a[i].x][a[i].y]=mp[a[i].y][a[i].x]=0;
			change(1,st,i-1,rk[a[i].x][a[i].y]);
		}
	}
	for(int i=1;i<=m;i++){
		if(mp[a[i].x][a[i].y]){
			int st=mp[a[i].x][a[i].y];
			mp[a[i].x][a[i].y]=mp[a[i].y][a[i].x]=0;
			change(1,st,m,rk[a[i].x][a[i].y]);
		}
	}
	dfs(1);
	for(int i=1;i<=m;i++){
		cout<<anss[i]<<endl;
	}
}

标签:int,线段,分治,tr,top,ans,sum,mod
From: https://www.cnblogs.com/jinjiaqioi/p/17717850.html

相关文章

  • 【边分治】P4565 [CTSC2018] 暴力写挂
    初涉边分治。大致就是按照一条边的两端来分类出两套点,然后对这个进行分治,有点是只用考虑两类集合,考虑贡献很简单!!反正就是比点分强。但是如果直接分治是假的,会被菊花图薄纱,需要进行对树的三度化,这个是左儿子右兄弟,但是貌似很少人叫这个,比较不适,然后我们就可以做了。建出来的边分......
  • 线段树合并的复杂度
    线段树合并的时间复杂度是\(O(m\logn)\)的(\(m\)为插入次数)。intmer(intx,inty){if(!x||!y)returnx^y;t[x]+=t[y];returnL[x]=mer(L[x],L[y]),R[x]=mer(R[x],R[y]),x;}因为每个点只会在自己被删除(情况1)或父亲被删除且自己未删除的(即合并时另一个子树为空,......
  • 简单分治快排问题解析(c++实现)
    这几天刷了需要使用分治快排思想去解决的几道比较好的题目,所以写下这篇博客用于复习和以后的复盘。什么是分治快排思想首先我们要知道什么是分治快排思想,这个思想其实就是在模拟实现qsort算法的时候使用的一个方法,在模拟实现qsort的时候,我们知道第一步是需要使用一个随意选择(三数取......
  • 【学习笔记】(26) cdq 分治 与 整体二分
    cdq分治基本思想我们要解决一系列问题,这些问题一般包含修改和查询操作,可以把这些问题排成一个序列,用一个区间[L,R]表示。分。递归处理左边区间\([L,M]\)和右边区间\([M+1,R]\)的问题。治。合并两个子问题,同时考虑到\([L,M]\)内的修改对\([M+1,R]\)内的查询产生的影......
  • 【线段树合并、虚树】P5327 [ZJOI2019] 语言
    终于1kAC了家人,感动吧。贺了很久,很累。前置题目:P3320[SDOI2015]寻宝游戏虚树的边权和:\[\sumdep_{a_x}-\sum_{x<n}dep_{a_x,a_{x+1}}-dep_{a_{1},a_{n}}\]考虑转化贡献,求过该点的链的并,最后再除以二即可。那么我们可以考虑维护以该点的子树的所有关键点以及......
  • poj 2528 Mayor's posters 线段树+离散化
    离散化处理要注意+1(看了HH大牛的博客懂的,以前自己的代码是不对的)例如数据:1311013610这样,普通离散化处理 {13610},然后此程序会操作成点染色,于是结果为2,但正确答案为3;HH大牛给出一种离散化方法:     如果相邻数字间距大于1的话,在其中加上任意一个数字,比如加......
  • 可持久化非确定状态AC自动分块维护线段平衡仙人掌优化最小费用最大流预处理混合图上莫
    P8946TheLostSymbol这种类型的dp的特点就是大部分转移形如\(f(i,j)\rightarrowf(i+1,j+1)\)之类的,并且当以上转移出现时原数组被清空,这就可以用一个deque来维护,然后对于全局赋值/全局加,需要对每个位置维护一个时间戳,并记录上一次赋值/加是什么时候,以便标记下传。(貌似......
  • 线段树
    线段树,一种非常通用的数据结构,多用于区间查询问题,虽然在时间和空间效率上都不如树状数组,但是因为其维护和操作更简单而受oier青睐为了加深记忆特此写篇博客大佬轻喷线段树,是一颗完全二叉树,由上到下维护,支持询问,更改等多种操作变种包括可持久化线段树及若干,本篇博客只提最简单......
  • F. Remainder Problem 根号分治
    Problem-F-Codeforces 题意:一个500000长度的数列,一开始都是0,进行q次操作,操作如下1,输入x,y,令a[x]+=y。2,输入x,y,输出对于sum(a[idx]),idx的条件是idx=x%y。做法:如果我们模拟做,那么第一种操作就是o(1),第二种操作就是o(n)。我们换种想法,建立一个二维数组b[x][y],表......
  • 练习:分治算法--有序数组寻找中位数
    题:给定两个长度为m和n有序组数array1和array2,请找出这个有序数组的中位数。'''eg.[1,3]和[5,6],中位数是4[1,2,5,8,9]和[2,3,4,5],中位数是4'''###直接方法,使用内置排序函数sort#时间复杂度最高:O((n+m)log(n+m)),空间复杂度:O(n+m)1classSolution(object):2deff......