以下分析基于这道题目。。和代码。。
讲解在后面别被题目吓到哈哈哈哈哈
#include<iostream>
#include<cstdio>
using namespace std;
int n,n_,m,a[200001<<2];char s;
void init(int w){
n=1;
while(n<w)n*=2;
for (int i=1;i<=2*n-1;i++) a[i]=0;
}
void update(int k,int b){
k+=n-1;
a[k]=b;
while(k>0) k/=2,a[k]=max(a[k*2],a[k*2+1]);
}
int query(int e,int b,int k,int l,int r){
if (r<e||b<l) return 0;
if (e<=l&&r<=b) return a[k];
else {
int vl=query(e,b,k*2,l,(l+r)/2);
int vr=query(e,b,k*2+1,(l+r)/2+1,r);
return max(vl,vr);
}
}
int main(){
while(~scanf("%d %d",&n_,&m)){
init(n_);int t1,t2;
for (int i=1;i<=n_;i++){
scanf("%d",&a[i+n-1]);
}
for (int i=n-1;i>0;i--) a[i]=max(a[i*2],a[i*2+1]);
for (int i=1;i<=m;i++){
scanf("%*c%c %d %d",&s,&t1,&t2);
if (s=='U') update(t1,t2);
if (s=='Q') printf("%d\n",query(t1,t2,1,1,n));
}
}
return 0;
}
(1)如果一共有9个结点, 先初始化,n=1, 直到n=16(9-16是空的,没话说)
while循环退出的时候是16啊,还是远远大于w了
然后需要二倍的放在上面,先初始化成0
(2)读入数据:
1就是1+16-1(16)
2就是2+16-2(17)
作为一棵树,它如果是2,上面结点的末尾数字是2的n次方-1 ,没错
随后更新,当然要自底向上,并且从n-1到1更新,
每次都是i=i*2和i*2+1里面的max
【注意】孜杰点(划掉)子结点分别是2*i和2*i-1
(3)随后,分别进行update和query
update:
小弟送礼一层一层递给大哥
啊,更新的时候,结点是k,值是b,反映过去当然k要+(n-1)
这样才能先到树上的结点。(先把树上的子结点,最小的那个叶子结点更新成为b)
随后,因为它是2*i或者2*i+1来的,/2就可以拿到那个根节点了,根节点于是也更新一下。
(4)l和r是你所在的提供服务的区间,e和b是想要查询的区间。
如果r<e,或者b<l,不好意思超出了服务范围,你的请求被拒绝了
如果这个区间正好囊括了【注意,是两边儿都相等】,就返回结点编号
结点编号,就是我们在上面初始化搞搞搞搞过的n(扩大了)
比如1-n区间,最小的就是结点编号为1的那个。
比如1-16,我们想查询3-7
【以下为演示】------
开始l=1 ,r=n,
先向左找e和b,(啊,其实就是左边的结点啊 )1的话就是*2 然后就是2, +1就是3,其实就是往下递归的意思呀。。。。
递归的时候这个结点能覆盖到的区间是什么呢...
l是肯定的,因为是左边的,
右边呢,下去和上来是一个原理,(1+16)/2=8
结点编号为2,维护的是1-8
其次,结点编号为3,8用过了,这里就是维护的9-16(虽然这个题里面9-16都是空的,但是数组也要这么长---也罢这个可忽略不看)
然后一直递归就好了呀,到了(3,4)的时候,e<=l r<=b 就返回了覆盖3 4 代表的结点的值a[k]
因为是最大值或者最小值,返回值里控制了一个max。
还是我家老抽写的代码简洁清楚,我看的蓝书白书一头雾水。。。。哈哈哈哈~ 待我把D维护一下去了
-------------------------------------------------------------------------------------------------
好了,D写完了,发现自己以前的理解有很多偏差
---上面写错了----
上面代码里面的n,就是最后一行的数量,nnn就是你实际有的结点的数量
for (int i = 1; i <= nnn; i++) { cin >> tmp; if (tmp == 0)tmp = inf; a[i + n - 1] = tmp; }
a[i+n-1] 可以用(nnn个,然后对应的是在线段树里面的下标)
查询的时候,如果想查3,就是(3,3,1,1,n)
后面三个数字的意思是,覆盖了1-n(底层)结点的在线段树里面结点编号是1
再比如,1 2 3 4 ,上面有4个,(nnn=4,n=4,但是一个4是最下面那一排有几个,一个是数据有多少)
所以1在线段树里面的结点编号已经是4了
读入的时候也是,因为上面有n-1个结点,所以维护的时候都是i+(n-1)
1(1-n)(1-4)
2(1-2 ) 3(3-4)
4(1-1) 5(2-2) 6(3-3) 7(4-4)
D题: 链接
用线段树维护最小值。
【注意】,上面的代码是最大值,因此是max,最小值就要改成min,初始化的时候也要改成inf
【q查询的时候找不到也要return inf】
【如何解决第一行就爆出“请按任意键继续”?】
这是因为stackoverflow……
开在里面就是容易爆,开全局数组。。。。
这个题目有个小坑,首先要处理0的问题,其次
【隐含条件】【隐含条件】【隐含条件】
如果是4 4 那么4必须要出现,因为每次至少维护一个区间。这时候。。。下标就很容易乱掉了,我改了那么久。。。瞎鸡儿写真的是
另外,作为线段树要开大4倍空间,因为首先,200000的话需药2^k至少是262144,然后要扩大一倍的话,。。。*&()所以我数组都放大了4倍过去的。。
4*maxn哦
后面的我已经头晕眼花了,…… 看都没看随便扔上去……
onj:“我看你是什么都不懂哦!!!!!”
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define maxn int(2e5+5)
#define inf 0x3f3f3f
int n, nnn, m, k, a[4*maxn];
char s;
int d1[4 * maxn];
int ddd[4 * maxn];
int d2[4*maxn];
int rec[4* maxn];
void init(int w) {
n = 1;
while (n<w)n *= 2;
for (int i = 1; i <= 2 * n - 1; i++) a[i] = inf;
}
void update(int k, int b) {
k += n - 1;
a[k] = b;
while (k>0) k /= 2, a[k] = min(a[k * 2], a[k * 2 + 1]);
}
int query(int e, int b, int k, int l, int r) {
if (r<e || b<l) return inf;
if (e <= l && r <= b) return a[k];
else {
int vl = query(e, b, k * 2, l, (l + r) / 2);
int vr = query(e, b, k * 2 + 1, (l + r) / 2 + 1, r);
return min(vl, vr);
}
}
int main() {
cin >> nnn >> k;
init(nnn);
//int inf = 200001;
int tmp;
for (int i = 1; i <= nnn; i++) {
cin >> tmp;
if (tmp == 0)tmp = inf;
a[i + n - 1] = tmp;
}
for (int i = n - 1; i>0; i--) a[i] = min(a[i * 2], a[i * 2 + 1]);
//这大概是初始化时两种必备工作
int cnt = 0;
int cnt0 = 0; int rec0 = 0;
for (int i = 1; i <=nnn; i++) {
//for every a[i]
int dd = a[i + n - 1];
if (a[i + n - 1] == inf) {
cnt0++;
rec0 = i + n - 1;
continue;
}
if (d1[dd] == 0) {
d1[dd] = i;//first appear
cnt++;
rec[cnt] = dd;//rec remembers how much numbers are there
d2[dd] = i;
}
else //d1[dd]!=0
d2[dd] = i;//last appear, others don't care
}
/////-------------------
////finds
/////-------------------
bool f = 1;
for (int i = 1; i <= cnt; i++) {
if (query(d1[rec[i] ] , d2[rec[i]] , 1, 1, n)<rec[i]) {
f = 0; break;
}
}
/////-------------------
////else
/////-------------------
if (d1[k] == 0&&cnt0==0)f = 0;
else if(d1[k]==0&&cnt0!=0){
a[rec0] = k;
}
//注意 我们要特判一下 如果最大的数字没出现过 并且也没有0 是不行的
if (f) {
cout << "YES" << endl;
if (a[1] == inf) {
for (int i = 1; i <= nnn; i++)if (i == nnn)cout << k << endl; else cout << k << " ";
}
else {
for (int i = 1; i <= nnn; i++) {
if (a[i + n - 1] != inf)
ddd[i] = a[i + n - 1];
else //如果是inf的话!
{
if (i == 1) {
int tmp = i + n - 1;
while (1) {
if (a[tmp] == inf)tmp++;
else break;
}
ddd[1] = a[tmp];
}
else { ddd[i] = ddd[i-1]; }
}
}
/*
for (int i = 1; i <= nnn; i++) {
if (a[i + n - 1] != inf)
ddd[i] = a[i + n - 1];//cout<<a[i+n-1];
else {
if (a[i + n - 1] == inf)a[i + n - 1] = 0;
if (i == n)ddd[i] = a[i + n - 2];
else ddd[i] = a[i + n];
}*/
for (int i = 1; i <= nnn; i++) {
//if ()ddd[i] = 0;
if (i == nnn)cout << ddd[i] << endl;
else cout << ddd[i] << " ";
}
}
}
else cout << "NO" << endl;
return 0;
}