ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

校门外的树——树状数组的简单应用

2022-01-26 18:04:31  阅读:168  来源: 互联网

标签:端点 树状 int 个数 门外 种树 数组 区间 我们


\(\color{blue}{题目}\)

\(传送门:\) https://vijos.org/p/1448
image

\(\color{blue}{分析}\)

\(\color{blue}{基础}\)

总而言之,不想敲线段树
好了,回归正题,首先我们用大暴力思维考虑一下,如果我们每次种树都给其所在区间填上种树的id,之后遍历找上所需区间的不同的树的id是不是就可以知道能见到多少种树了呢?
确实如此,当然这无疑是通不过的。我们反过来想:如果按上述做法我们是不是不需要每次种树的种类不同、种树时是按连续区间来种这两个条件呢?所以说,正确的做法就应该是利用上述的两个条件来做。
Q1:种树时是按连续区间来种意味着什么呢?
肉眼可见地意味着如果出现\([a,b,c,d,e]\)这样一个区间(字母代表每个点的树的数量)那么\(a,e\)最多只会有\(min(b,c,d)\)种相同的树种
Q1:每次种树的种类不同意味着什么呢?
意味着没有两次会种相同的树种,意味着如果一个区间出现\([1,0,3,0,1]\)(字母代表每个点的树的数量),那我们就知道这个区间种了5种树(这种情况我们就无需知道树的id了)

\(\color{blue}{进阶}\)

因此我们的目标就转化为在解题的过程种消去树的id这一不必记录的变量。
之后我们考虑如果我们知道\([l,r1],[l,r2](r1<r2)\),我们可不可以知道\([r1+1,r2]\)呢?我们是不可以知道的。但这是一种思维(只考虑区间本身而不特意关注区间内的每个点)
这时我们引进树状数组的概念:

\[C[0] = A[0] = 0\\ C[1] = A[1]\\ C[2] = A[1]+A[2]\\ C[3] = A[3]\\ C[4] = A[1]+A[2]+A[3]+A[4]\\ C[5] = A[5]\\ C[6] = A[5]+A[6]\\ C[7] = A[7]\\ C[8] = A[1]+A[2]+A[3]+A[4]+A[7]+A[8]\\ ... \]

整理一下:
\(C[2*i-1] = A[2*i-1],C[0] = 0\)
\(C[2*i] = A[2*i]+A[2*i-1]+C[2*(i-1)]\)
显然数组C和数组A是相互等价的(由一个可以推出来另一个)
那我们继续考虑区间问题,比如我们要求区间内树的个数而非树的种类,那我们是不是很快就可以算完了?

比如我要求[4,7]的树的个数,我只需要找到[1,3]的树的个数和[1,7]的树的个数
然后[1,3]的树的个数就等于C[4]-A[4],[1,7]的树的个数就等于C[8]-A[8]

但是现在问题不同,树的种类是一个相较而言更加棘手的问题,这时我们需要用到一些小的技巧。
首先区间只有其两个端点决定,我们为每一个端点建一个C(下面代码里是sum)如果要在端点a种树,让包含A[a]的C加一

比如我们在[4,6]种树,对于4(左端点),我们让c1[4]、c1[8]、...都自增,
对于6(右端点),我们让c2[6]、c2[8]、....都自增,
这样我们找5时用式子
{c1[5]+c1[4](去掉lowbit)}-{c2[5]+c2[4](去掉lowbit)}
就可以找到。

证明可以通过数归来证,博主也尚未想到更好的办法,只能感慨人与人之间的差距罢了,像我这样只能努力采集他们的智慧来提升自己了吧。
以下为AC code:

#include<bits/stdc++.h>
using namespace std;
#define INIT ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define N 50005
int n,m;
int sum1[N];
int sum2[N];
int lowbit(int x){
	return x&(-x);
}
void add(int pos,int flag){
	int *t=sum1;
	if(flag==2) t = sum2;
	while(pos<=n){
		t[pos]++;
		pos+=lowbit(pos);
	}
}
int query(int pos,int flag){
	int sum =0;
	int *t = sum1;
	if(flag==2) t = sum2;
	while(pos){
		sum+=t[pos];
		pos-=lowbit(pos);
	}
	return sum;
}
int main(){
	INIT;
	//freopen("in.txt","r",stdin);
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int f;
		cin>>f;
		int l,r;
		cin>>l>>r;
		if(f==1){
			add(l,1);
			add(r,2);	
		}else{
			cout<<query(r,1)-query(l-1,2)<<endl;
		}
	}
	return 0;
} 

标签:端点,树状,int,个数,门外,种树,数组,区间,我们
来源: https://www.cnblogs.com/OceanCT/p/15847616.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有