您的位置:首页 > 其它

|算法讨论|线段树1(大白书版本) 学习笔记

2017-01-22 15:27 369 查看
常用方法:

1、点修改,区间查询

2、区间增加,区间查询

3、区间修改,区间查询

4、混合多种修改,区间查询

5、离散化操作

6、二维线段树

1、点修改,区间查询

input:
10 6
5 4 8 9 7 2 4 1 5 7
1 1 10
0 8 6
1 1 10
1 2 5
0 4 -8
1 1 5
//n m
//n个数,表示线段树节点的初始值
//m行,每行一个指令
//0 p v,修改p节点为v
//1 x y,求x~y区间最小值

output:
1
2
4
-8
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<set>
#include<string>
#define ms(i,j) memset(i,j,sizeof i);
using namespace std;
const int MAXN = 1000 + 5;
int n,m;
int mino[MAXN];//最小值
int p,v;
int update(int o, int l, int r)//更新a[p] = v;
{
int m = (l+r)/2;
int lc = o*2, rc = o*2+1;
if (l==r) mino[o] = v;
else
{
if (p<=m) update(lc, l, m); else update(rc, m+1, r);
mino[o] = min(mino[lc], mino[rc]);
}
}
int x,y;
int query(int o, int l ,int r)//查询x,y区间最小值
{
int m = (l+r)/2;
int lc = o*2, rc = o*2+1;
int ans = 100000000;
if (x<=l&&r<=y) return mino[o];
if (x<=m) ans = min(ans, query(lc, l, m));
if (m<y) ans = min(ans, query(rc, m+1, r));
return ans;
}
int main()
{
freopen("st.in", "r", stdin);freopen("st.out", "w", stdout);
scanf("%d%d", &n ,&m);
for (int i=1;i<=n;i++)//输入初值
{
scanf("%d", &v);
p = i;
update(1,1,n);//利用修改值来赋值初值
}
for (int i=1;i<=m;i++)//输入m个指令
{
int type;
scanf("%d", &type);
if (type==1)//求x,y的最小值
{
scanf("%d%d", &x, &y);
printf("%d\n", query(1,1,n));
} else//修改a[p] = v;
{
scanf("%d%d", &p, &v);
update(1,1,n);
}
}
fclose(stdin);fclose(stdout);
return 0;
}


2、区间增加,区间查询

input:
10 10
5 4 8 9 7 2 4 1 5 7
1 1 10
2 1 10
3 1 10
0 6 7 10
2 2 8
3 2 5
0 4 6 -7
3 4 8
0 10 10 -17
2 7 10
//n m
//n个数,表示线段树节点的初始值
//m行,每行一个指令
//0 x y v,把x~y增加v
//1 x y,求x~y区间和
//2 x y,求x~y区间最小值
//3 x y,求x~y区间最大值
output:
52
1
9
1
9
14
-10
#include<cstdio>
#include<algorithm>
#include<cstring>
#define ms(i,j) memset(i,j,sizeof i);
using namespace std;
const int MAXN = 1000 + 5;
int n,m;
int _max, _min, _sum;
int maxv[MAXN], minv[MAXN], addv[MAXN], sumv[MAXN];
int mt(int o, int l, int r)//更新结点信息
{
int m = (l+r)/2;
int lc = o*2, rc = o*2+1;
if (r>l)
{
sumv[o] = sumv[lc]+sumv[rc];
minv[o] = min(minv[lc], minv[rc]);
maxv[o] = max(maxv[lc], maxv[rc]);
}
minv[o] += addv[o]; maxv[o] += addv[o]; sumv[o] += (r-l+1)*addv[o];
}
int x,y,v;
int update(int o, int l, int r)//使x,y区间都加上v
{
int m = (l+r)/2;
int lc = o*2, rc = o*2+1;
if (x<=l&&r<=y)
{
addv[o] = v;
} else
{
if (x<=m) update(lc, l, m);
if (m<y) update(rc, m+1, r);
}
mt(o, l ,r);
}
int query(int o, int l, int r, int add)//查询x,区间信息
{
int m = (l+r)/2;
int lc = o*2, rc = o*2+1;
if (x<=l&&r<=y)
{
_sum += sumv[o] + add * (r-l+1);
_min = min(_min, minv[o]+add);
_max = max(_max, maxv[o]+add);
}else
{
if (x<=m) query(lc, l, m,add+addv[o]);
if (m<y) query(rc, m+1, r,add+addv[o]);
}
}
int main()
{
freopen("st.in", "r", stdin);freopen("st.out", "w", stdout);
scanf("%d%d", &n, &m);
for (int i=1;i<=n;i++)//输入初值
{
scanf("%d", &v);
x = y = i;
update(1,1,n);
}
for (int i=1;i<=m;i++)//输入指令
{
int type;
scanf("%d", &type);
if (type==0)
{
scanf("%d%d%d", &x, &y, &v);
update(1,1,n);
} else
{
scanf("%d%d", &x, &y);
_sum=_max=0; _min=100000000;
query(1,1,n,0);
switch(type)
{
case 1: printf("%d\n", _sum); break;
case 2: printf("%d\n", _min); break;
case 3: printf("%d\n", _max); break;
}
}
}
fclose(stdin);fclose(stdout);
return 0;
}


3、区间修改,区间查询

input:
10 10
5 4 8 9 7 2 4 1 5 7
1 1 10
2 1 10
3 1 10
0 6 7 10
2 2 8
3 2 5
0 4 6 2
3 4 8
0 10 10 17
2 7 10
//n m
//n个数,表示线段树节点的初始值
//m行,每行一个指令
//0 x y v,把x~y修改为v(v>=0)
//1 x y,求x~y区间和
//2 x y,求x~y区间最小值
//3 x y,求x~y区间最大值
output:
52
1
9
1
9
10
1
#include<cstdio>
#include<algorithm>
#include<cstring>
#define ms(i,j) memset(i,j,sizeof i);
using namespace std;
const int MAXN = 1000 + 5;
int _max, _min, _sum;
int setv[MAXN], minv[MAXN], maxv[MAXN], sumv[MAXN];
int n,m;
int pushdown(int o)//标记下传
{
int lc = o*2, rc = o*2+1;
if (setv[o]>=0)
{
setv[lc] = setv[rc] = setv[o];
setv[o] = -1;
}
}
int mt(int o, int l, int r)//更新结点信息
{
int m = (l+r)/2;
int lc = o*2, rc = o*2+1;
if (r>l)
{
sumv[o] = sumv[lc] + sumv[rc];
minv[o] = min(minv[lc], minv[rc]);
maxv[o] = max(maxv[lc], maxv[rc]);
}
if (setv[o]>=0) {minv[o]=maxv[o]=setv[o]; sumv[o] = setv[o]*(r-l+1);}
}
int x,y,v;
int update(int o, int l, int r)//修改x,y区间值为v
{
int m = (l+r)/2;
int lc = o*2, rc = o*2+1;
if (x<=l&&r<=y)
{
setv[o] = v;
} else
{
pushdown(o);
if (x<=m) update(lc, l, m); else mt(lc, l, m);
if (m<y) update(rc,m+1,r); else mt(rc, m+1,r);
}
mt(o,l,r);
}
int query(int o, int l, int r)//查询x,y区间信息
{
int m = (l+r)/2;
int lc = o*2, rc = o*2+1;
if (setv[o]>=0)
{
_sum += setv[o] * (min(r,y)-max(l,x)+1);
_min = min(_min, setv[o]);
_max = max(_max, setv[o]);
} else if (x<=l&&r<=y)
{
_sum += sumv[o];
_min = min(_min, minv[o]);
_max = max(_max, maxv[o]);
} else
{
if (x<=m) query(lc, l, m);
if (m<y) query(rc,m+1,r);
}
}
int main()
{
freopen("st.in", "r", stdin);freopen("st.out", "w", stdout);
scanf("%d%d", &n, &m);
ms(setv,-1);//负数说明没有set标记
for (int i=1;i<=n;i++)//输入初值
{
scanf("%d", &v);
x = y = i;
update(1,1,n);
}
for (int i=1;i<=m;i++)//输入指令
{
int type;
scanf("%d", &type);
if (type==0)
{
scanf("%d%d%d", &x, &y, &v);
update(1,1,n);
} else
{
scanf("%d%d", &x, &y);
_sum=0;_max=-100000000; _min=100000000;
query(1,1,n);
switch(type)
{
case 1: printf("%d\n", _sum); break;
case 2: printf("%d\n", _min); break;
case 3: printf("%d\n", _max); break;
}
}
}
fclose(stdin);fclose(stdout);
return 0;
}

4、混合多种修改,区间查询

(1) 修改+增加

input:
10 10
1 2 3 4 5 6 7 8 9 10
2 1 10
0 2 4 5
2 1 6
1 2 7 2
2 3 9
1 1 10 3
2 8 9
0 2 6 2
2 3 7
2 10 10
//n,m
//n个数,表示线段树初值
//m行,每行一个指令
//0 x y v          x,y区间值全部修改为v(v>=0)
//1 x y v          x,y区间值全部增加v
//2 x y            输出x,y区间最小值、最大值、区间和
output:
min
e300
:1 max:10 sum:55
min:1 max:6 sum:27
min:7 max:9 sum:55
min:11 max:12 sum:23
min:2 max:12 sum:20
min:13 max:13 sum:13
#include<cstdio>
#include<algorithm>
#include<cstring>
#define ms(i,j) memset(i,j,sizeof i);
using namespace std;
const int MAXN = 1000 + 5;
int n,m;
int _max, _min, _sum;
int maxv[MAXN], minv[MAXN], sumv[MAXN], addv[MAXN], setv[MAXN];
int mt(int o, int l, int r)
{
int m = (l+r)/2;
int lc = o*2, rc = o*2+1;
sumv[o] = minv[o] = maxv[o] = 0;
if (setv[o]>=0)
{
sumv[o] = setv[o]*(r-l+1);
minv[o] = maxv[o] = setv[o];
}  else if (r>l)
{
sumv[o] = sumv[lc]+sumv[rc];
minv[o] = min(minv[lc], minv[rc]);
maxv[o] = max(maxv[lc], maxv[rc]);
}
if (addv[o]!=0) {sumv[o] += addv[o] * (r-l+1); minv[o] += addv[o]; maxv[o] += addv[o];}
}
int pushdown(int o)
{
int lc = o*2, rc = o*2+1;
if (setv[o]>=0)
{
setv[lc] = setv[rc] = setv[o];
addv[lc] = addv[rc] = 0;//传下去一定要删除add标记
setv[o] = -1;
}
if (addv[o]!=0)
{
addv[lc] += addv[o];
addv[rc] += addv[o];
addv[o] = 0;
}
}
int x,y,v;
int update_add(int o, int l, int r)
{
int m = (l+r)/2;
int lc = o*2, rc = o*2+1;
if (x<=l&&r<=y) addv[o] += v;
else
{
if (x<=m) update_add(lc, l, m);
if (m<y) update_add(rc, m+1, r);
}
mt(o,l,r);
}
int update_set(int o, int l, int r)
{
int m = (l+r)/2;
int lc = o*2, rc = o*2+1;
if (x<=l&&r<=y)
{
setv[o] = v;
addv[o] = 0;
}
else
{
pushdown(o);
if (x<=m) update_set(lc, l, m); else mt(lc, l, m);
if (m<y) update_set(rc, m+1, r); else mt(rc, m+1, r);
}
mt(o,l,r);
}
int query(int o, int l, int r, int add)
{
int m = (l+r)/2;
int lc = o*2, rc = o*2+1;
if (setv[o]>=0)
{
_sum += (setv[o]+add+addv[o])*(min(y,r)-max(x,l)+1);
_min = min(_min, setv[o]+add+addv[o]);
_max = max(_max, setv[o]+add+addv[o]);
} else if (x<=l&&r<=y)
{
_sum += sumv[o]+add*(r-l+1);
_min = min(_min, minv[o]+add);
_max = max(_max, maxv[o]+add);
} else
{
if (x<=m) query(lc, l, m,add+addv[o]);
if (m<y) query(rc, m+1, r,add+addv[o]);
}
}
int main()
{
freopen("st.in", "r", stdin);freopen("st.out", "w", stdout);
scanf("%d%d", &n, &m);ms(setv, -1);
for (int i=1;i<=n;i++)
{
scanf("%d", &v);
x = y = i;
update_set(1,1,n);
}
for (int i=1;i<=m;i++)
{
int type;
scanf("%d", &type);
if (type==0)
{
scanf("%d%d%d", &x, &y, &v);
update_set(1,1,n);
} else if (type==1)
{
scanf("%d%d%d", &x, &y, &v);
update_add(1,1,n);
} else
{
scanf("%d%d", &x, &y);
_sum=_max=0; _min=100000000;
query(1,1,n,0);
printf("min:%d max:%d sum:%d\n", _min, _max, _sum);
}
}
fclose(stdin);fclose(stdout);
return 0;
}
(2)增加+乘法
https://www.luogu.org/problem/show?pid=3373
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: