首页 > 其他分享 >树套树从入门到去世

树套树从入门到去世

时间:2024-03-20 21:25:08浏览次数:20  
标签:入门 limits int sum times 数组 树套 dp 去世

如何实现数据结构的嵌套?

首先我们知道,单个数据结构是对一些存有某些信息的节点进行操作,从而达到目的。

然后我们将这些节点换成另一种数据结构,更改的时候对某些数据结构进行修改,就可以实现嵌套。

二维树状数组

其实是最好写的一种树套树。

单点修改,区间查询

就像上文说的一样,我们对每一行开一个树状数组,维护该列的信息,再使用一个树状数组来维护这些树状数组,单点修改时直接在大树状数组里修改小树状数组即可。

区间查询做一个差分就好了。

int lb(int x){
	return x&(-x);
}
void update(int x,int y,int k){
	for(int i=x;i<=n;i+=lb(i)){
		for(int j=y;j<=m;j+=lb(j)){
			c[i][j]+=k;
		}
	}
}
int query(int x,int y){
	int res=0;
	for(int i=x;i;i-=lb(i)){
		for(int j=y;j;j-=lb(j)){
			res+=c[i][j];
		}
	}
	return res;
} 
void work(int x,int y,int x_,int y_,int k){
	update(x,y,k),update(x,y_+1,-k),update(x_+1,y,-k),update(x_+1,y_+1,k);
}
int getans(int x,int y,int x_,int y_){
	return query(x_,y_)-query(x_,y-1)-query(x-1,y_)+query(x-1,y-1);
}

区间修改,单点查询

想一下我们利用树状数组维护一维序列时的区间修改,单点查询时怎么做?

直接维护原序列的差分序列,然后就可以转换为单点修改,区间查询了。

二维信息也是如此。注意容斥即可。

int lb(int x){
	return x&(-x);
}
void update(int x,int y,int k){
	for(int i=x;i<=n;i+=lb(i)){
		for(int j=y;j<=m;j+=lb(j)){
			c[i][j]+=k;
		}
	}
}
int query(int x,int y){
	int res=0;
	for(int i=x;i;i-=lb(i)){
		for(int j=y;j;j-=lb(j)){
			res+=c[i][j];
		}
	}
	return res;
} 
void work(int x,int y,int x_,int y_,int k){
	update(x,y,k),update(x,y_+1,-k),update(x_+1,y,-k),update(x_+1,y_+1,k);
}
int getans(int x,int y,int x_,int y_){
	return query(x_,y_)-query(x_,y-1)-query(x-1,y_)+query(x-1,y-1);
}

区间修改,区间查询 / 上帝造题的七分钟

还是考虑维护差分数组。

考虑二维差分数组 \(b\) 的定义,不难得到左上角为 \((1,1)\),右下角为 \((x,y)\) 的矩阵的和为 \(\sum\limits_{i=1}\limits^{x}\sum\limits_{j=1}\limits^{y}\sum\limits_{k=1}\limits^{i}\sum\limits_{l=1}\limits^{j} b_{k,l}.\)

于是我们集中注意力,发现每个 \(b_{i,j}\) 的出现次数仍然有规律(\(b_{i,j}\) 出现了 \((x-i+1)\times(y-j+1)\) 次),于是有 \(\sum\limits_{i=1}\limits^{x}\sum\limits_{j=1}\limits^{y}\sum\limits_{k=1}\limits^{i}\sum\limits_{l=1}\limits^{j} b_{k,l}=\sum\limits_{i=1}\limits^{x}\sum\limits_{j=1}\limits^y b_{i,j} \times (x-i+1) \times (y-j+1).\)

括号乘开,得:

\(\sum\limits_{i=1}\limits^{x}\sum\limits_{j=1}\limits^y b_{i,j} \times (x-i+1) \times (y-j+1) = b_{i,j} \times(xy+x+y+1) - b_{i,j} \times i \times (y+1) - b_{i,j} \times j \times (x+1) + b_{i,j} \times i \times j.\)

于是开四个树状数组,分别维护 \(b_{i,j}\),\(b_{i,j} \times i\),\(b_{i,j} \times j\) 和 \(b_{i,j} \times i \times j\) 即可。

int lb(int x){
	return x&(-x);
}
void update(int x,int y,int k){
	for(int i=x;i<=n;i+=lb(i)){
		for(int j=y;j<=m;j+=lb(j)){
    		c1[i][j]+=k;
    		c2[i][j]+=k*x;
    		c3[i][j]+=k*y;
    		c4[i][j]+=k*x*y;
		}
	}
}
int query(int x,int y){
	int res=0;
	for(int i=x;i;i-=lb(i)){
		for(int j=y;j;j-=lb(j)){
			res+=(x+1)*(y+1)*c1[i][j]-(x+1)*c3[i][j]-(y+1)*c2[i][j]+c4[i][j];
		}
	}
	return res;
} 
void work(int x,int y,int x_,int y_,int k){
	update(x,y,k),update(x,y_+1,-k),update(x_+1,y,-k),update(x_+1,y_+1,k);
}
int getans(int x,int y,int x_,int y_){
	return query(x_,y_)-query(x_,y-1)-query(x-1,y_)+query(x-1,y-1);
}

Back From Summer '17 P6: Crowded Cities

乍一看,是一个三维偏序,可以使用 CDQ 分治解决。但是注意到每一维都只有 \(5000\),于是就给了我们使用二维树状数组的机会。

首先我们按 \(H\) 从小到大排序,就可以消掉一维。令 \(dp_i\) 为以 \(i\) 结尾的容纳人数最大值,那么 \(dp_i = \max\limits_{L_j \le L_i,W_j \le W_i} dp_j + P_i\)。

我们把 \(dp_i\) 丢到点 \((L_i,W_i)\) 上,这样每次寻找 \(\max\limits_{L_j \le L_i,W_j \le W_i} dp_j\) 的过程相当于在树状数组上查询一个前缀的最大值,直接转移即可。

注意这题还卡空间,树状数组时不可以开 long long 的。这很好解决,用它维护下标,而非值即可。同时这样写的话输出方案也会简单很多。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxv=5000+10,V=5500,maxn=1e5+10;
ll n,dp[maxn],ans,lst[maxn],ansi;
int c[maxv][maxv];
struct building{
	ll a,b,c,p,id;
	bool operator <(building _){
		if(c==_.c){
			if(b==_.b){
				return a<_.a;
			}
			return b<_.b;
		}
		return c<_.c;
	}
}x[maxn];
int lb(int x){
	return x&(-x);
}
void update(int x,int y,int pos){
	for(int i=x;i<=V;i+=lb(i)){
		for(int j=y;j<=V;j+=lb(j)){
			if(dp[pos]>dp[c[i][j]]){
				c[i][j]=pos;
			}
		}
	}
}
int query(int x,int y){
	pair<ll,int> res;
	res={0,0};
	for(int i=x;i;i-=lb(i)){
		for(int j=y;j;j-=lb(j)){
			if(dp[c[i][j]]>res.first){
				res={dp[c[i][j]],c[i][j]};
			}
		}
	}
	return res.second;
}
signed main(){
//	freopen("a.txt","r",stdin);
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>x[i].a>>x[i].b>>x[i].c>>x[i].p;
		if(x[i].a<x[i].b) swap(x[i].a,x[i].b);
		x[i].id=i;
	}
	sort(x+1,x+n+1);
	for(int i=1;i<=n;i++){
		int maxi=query(x[i].a,x[i].b);
		dp[i]=dp[maxi]+x[i].p;
		lst[i]=maxi;
		if(ans<dp[i]) ans=dp[i],ansi=i;
		update(x[i].a,x[i].b,i);
	}
	ll tmp=ansi,cnt=0;
	cout<<ans<<endl;
	while(tmp){
		cnt++;
		tmp=lst[tmp];
	}
	cout<<cnt<<endl;
	while(ansi){
		cout<<x[ansi].id<<" ";
		ansi=lst[ansi];
	}
	return 0;
}

标签:入门,limits,int,sum,times,数组,树套,dp,去世
From: https://www.cnblogs.com/luqyou/p/18086110

相关文章

  • 爬虫入门系列-HTML基础语法
    ......
  • BUU之misc入门
    BUU之misc题目你竟然赶我走给了个附件,就是这个表情包。。010打开,Ctrl+F搜flag直接秒了flagISflag{stego_is_s0_bor1ing}题目来首歌吧打开附件是一首歌,魔性小猫,听着听着发现有摩斯电码的声音,打开Audacity看看发现音频分了左右声道对应----.题目金2+1胖附件......
  • 【飞浆AI实战】交通灯检测:手把手带你入门PaddleDetection,从训练到部署
    前言本次分享将带领大家从0到1完成一个目标检测任务的模型训练评估和推理部署全流程,项目将采用以PaddleDetection为核心的飞浆深度学习框架进行开发,并总结开发过程中踩过的一些坑,希望能为有类似项目需求的同学提供一点帮助。项目背景和目标背景:目标检测是计算机视觉的一......
  • Spring入门案例
    一、下载Spring5jar包官网地址:https://spring.io/版本目录:https://spring.io/projects/spring-framework#learn下载地址:https://repo.spring.io/ui/native/release/org/springframework/spring/二、新建java工程2.1新建项目2.2导入jar包必需的jar包2.2.1新建lib......
  • Android第一行代码——快速入门 Kotlin 编程(3.7 Kotlin课堂:标准函数和静态方法)
    目录3.7        Kotlin课堂:标准函数和静态方法3.7.1    标准函数with、run和apply3.7        Kotlin课堂:标准函数和静态方法        现在我们即将进入本书首次的Kotlin课堂,之后的几乎每一章中都会有这样一个环节。虽说目前你已经可......
  • Android第一行代码——快速入门 Kotlin 编程(3.6 Activity 的最佳实践)
    目录3.6        Activity的最佳实践3.6.1    知晓当前是在哪一个Activity3.6.2    随时随地退出程序 3.6.3    启动Activity的最佳写法3.6        Activity的最佳实践        关于Activity,你已经掌握了非常多......
  • 一个入门级python爬虫教程详解
    前言当你需要每天对Excel做大量重复的操作,如果只靠人工来做既浪费时间,又十分枯燥,好在Python为我们提供了许多操作Excel的模块,能够让我们从繁琐的工作中腾出双手。今天就和大家分享一个快速处理Excel的模块openpyxl,它的功能相对与其他模块更为齐全,足够应对日常出......
  • .NET Emit 入门教程:第一部分:Emit 介绍
    前言:Emit 是开发者在掌握反射的使用后,进阶需要的知识,它能显著的改善因反射带来的性能影响。目前能搜到的Emit的相关文章,都是一篇系列,通常推荐对照着反绎后的 IL 编写Emit代码,门槛太高。借着优化CYQ.Data 时使用Emit 的心得体会及记忆,写个简单的入门教程,以帮助后来者......
  • openGauss资源池化开发者入门指南(二)
    openGauss资源池化开发者入门指南(二)一、内容简介openGauss资源池化是openGauss推出的一种新型的集群架构.通过DMS和DSS组件,实现集群中多个节点的底层存储数据共享和节点间的内存实时共享达到节省底层存储资源以及集群内部支持一写多读且可以实时一致性读的目的.本......
  • openGauss资源池化开发者入门指南(一)
    openGauss资源池化开发者入门指南(一)一、内容简介openGauss资源池化是openGauss推出的一种新型的集群架构.通过DMS和DSS组件,实现集群中多个节点的底层存储数据共享和节点间的内存实时共享达到节省底层存储资源以及集群内部支持一写多读且可以实时一致性读的目的.本系......