HDU 1166 敌兵布阵
Description
区间1~n初始有一些人,某个区间可以增加人数、减少人数、询问人数。
线段树
1.N * 4 + 1的空间(有N * 2节省空间的方法)
2.用 位运算符时注意其优先级低,用 (num << 1) + 1 或 num << 1 | 1
3.更新末操作:a[num].val = a[num << 1].val + a[(num << 1) + 1].val;
4.单点更新、单点查询、区间查询都可套用模板,但区间更新分①普通更新②lazy标记
Code of ordinary Segment tree
#include <map>
#include <cstdio>
#include <iostream>
using namespace std;
const int N = 50005;
map<string, int> mp;
struct node
{
int L, R, val;
}a[(N << 2) + 1];
void init(int num, int l, int r)///r = 6,r = 10数组并没有充分利用
{
a[num].L = l;
a[num].R = r;
a[num].val = 0;
if(l == r)
return ;
int mid = (l + r) >> 1;
init(num << 1, l, mid);///mid被分到左孩子
init((num << 1) + 1, mid + 1, r);///忽略了位运算符优先级低
}
///设flag为1对应增加,为0对应减少
void update(int num, int l, int r, bool flag, int tot)
{
if(a[num].L == l && a[num].R == r)///找到区间,去做任务
{
if(flag)
a[num].val += tot;
else
a[num].val -= tot;
return ;///这里的return都能忘?!
}
if(a[num].L == a[num].R)///未找到区间,返回上一级,程序向下运行
return ;
int mid = (a[num].L + a[num].R) >> 1;
if(mid >= r)
update(num << 1, l, r, flag, tot);
else if(mid < l)///此处无等号(mid被分到左孩子)
update((num << 1) + 1, l, r, flag, tot);
else
{
update(num << 1, l, mid, flag, tot);
update((num << 1) + 1, mid + 1, r, flag, tot);
}
a[num].val = a[num << 1].val + a[(num << 1) + 1].val;
}
int query(int num, int l, int r)
{
if(a[num].L == l && a[num].R == r)///找到区间,去做任务
return a[num].val;
if(a[num].L == a[num].R)
return 0;
int mid = (a[num].L + a[num].R) >> 1;
if(mid >= r)
return query(num << 1, l, r);
else if(mid < l)
return query((num << 1) + 1, l, r);
else
return (query(num << 1, l, mid) + query((num << 1) + 1, mid + 1, r));
}
int main()
{
int t;
string s[4];
s[0] = "End";///字符串对应时,尽量控制只比较第一个字符
s[1] = "Add";
s[2] = "Sub";
s[3] = "Query";
for(int i = 0; i < 4; ++i)
mp[ s[i] ] = i;
scanf("%d", &t);
int tot = t;
while(t--)
{
int n, tem;
scanf("%d", &n);
init(1, 1, n);
for(int i = 1; i <= n; ++i)
{
scanf("%d", &tem);
update(1, i, i, 1, tem);
}
printf("Case %d:\n", tot - t);
string ch;
while(cin >> ch)
{
bool flag = 0;
int x, y;
switch(mp[ch])
{
case 0:
flag = 1;
break;
case 1:
scanf("%d%d", &x, &y);
update(1, x, x, 1, y);
break;
case 2:
scanf("%d%d", &x, &y);
update(1, x, x, 0, y);
break;
case 3:
scanf("%d%d", &x, &y);
cout << query(1, x, y) << '\n';
break;
}
if(flag)
break;
}
}
return 0;
}
Code of Improved Segment tree(by lazy array)
1.区间结构体中除L、R、val,増一个lazy
2.init时lazy赋0
3.更新时,若当前区间是目标区间,更新val顺便,更新lazy
若当前区间不是目标区间,数据下传:down(传完记得lazy清零)
#include <map>
#include <cstdio>
#include <iostream>
using namespace std;
const int N = 50005;
map<string, int> mp;
struct node
{
int L, R, val, lazy;
} a[N << 2 | 1];
void down(int x)///将x区间数据下传
{
///子区间继承父区间的lazy以便下传
///更新子区间的val
a[x << 1].lazy += a[x].lazy;
a[x << 1].val += a[x].lazy;
a[x << 1 | 1].lazy += a[x].lazy;
a[x << 1 | 1].val += a[x].lazy;
a[x].lazy = 0;///下传完了lazy清零
}
void init(int num, int l, int r)///r = 6,r = 10数组并没有充分利用
{
a[num].L = l;
a[num].R = r;
a[num].val = 0;
a[num].lazy = 0;
if(l == r)
return ;
int mid = (l + r) >> 1;
init(num << 1, l, mid);///mid被分到左孩子
init(num << 1 | 1, mid + 1, r);///忽略了位运算符优先级低
}
///设flag为1对应增加,为0对应减少
void update(int num, int l, int r, bool flag, int tot)
{
if(a[num].L == l && a[num].R == r)///找到区间,去做任务
{
if(flag)
{
a[num].val += tot;
a[num].lazy += tot;
}
else
{
a[num].val -= tot;
a[num].lazy -= tot;
}
return ;
}
if(a[num].L == a[num].R)///未找到区间,返回上一级,程序向下运行
return ;
down(num);///此区间非目标区间,数据下传
int mid = (a[num].L + a[num].R) >> 1;
if(mid >= r)
update(num << 1, l, r, flag, tot);
else if(mid < l)///此处无等号(mid被分到左孩子)
update(num << 1 | 1, l, r, flag, tot);
else
{
update(num << 1, l, mid, flag, tot);
update(num << 1 | 1, mid + 1, r, flag, tot);
}
a[num].val = a[num << 1].val + a[num << 1 | 1].val;
}
int query(int num, int l, int r)
{
if(a[num].L == l && a[num].R == r)///找到区间,去做任务
return a[num].val;
if(a[num].L == a[num].R)
return 0;
int mid = (a[num].L + a[num].R) >> 1;
if(mid >= r)
return query(num << 1, l, r);
else if(mid < l)
return query(num << 1 | 1, l, r);
else
return (query(num << 1, l, mid) + query(num << 1 | 1, mid + 1, r));
}
int main()
{
int t;
string s[4];
s[0] = "End";///字符串对应时,尽量控制只比较第一个字符
s[1] = "Add";
s[2] = "Sub";
s[3] = "Query";
for(int i = 0; i < 4; ++i)
mp[ s[i] ] = i;
scanf("%d", &t);
int tot = t;
while(t--)
{
int n, tem;
scanf("%d", &n);
init(1, 1, n);
for(int i = 1; i <= n; ++i)
{
scanf("%d", &tem);
update(1, i, i, 1, tem);
}
printf("Case %d:\n", tot - t);
string ch;
while(cin >> ch)
{
bool flag = 0;
int x, y;
switch(mp[ch])
{
case 0:
flag = 1;
break;
case 1:
scanf("%d%d", &x, &y);
update(1, x, x, 1, y);
break;
case 2:
scanf("%d%d", &x, &y);
update(1, x, x, 0, y);
break;
case 3:
scanf("%d%d", &x, &y);
cout << query(1, x, y) << '\n';
break;
}
if(flag)
break;
}
}
return 0;
}
Try to save time and memory
1.张昆玮线段树:循环代替递归 张昆玮线段树
2.N * 2空间:空间节省
树状数组
三个函数:lowbit、单点更新、区间查询
Code of Binary Index Tree
#include <map>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 50005;
map<string, int> mp;
int n;///区间上限
int tree[N];///树状数组C[N]
///原始数组A[N]假设存在
int lowbit(int x)
{
return x & (-x);
}
///单点更新
void update(int x, int y)///修改A[x]对应相关C[]的更新
{
for(int i = x; i <= n; i += lowbit(i))///要求n为全局变量
tree[i] += y;
}
///区间查询
int get_sum(int x)///求A[]前x项的和
{
int ans = 0;
for(int i = x; i >= 1; i -= lowbit(i))
ans += tree[i];
return ans;
}
int main()
{
int t;
string s[4];
s[0] = "End";///字符串对应时,尽量控制只比较第一个字符
s[1] = "Add";
s[2] = "Sub";
s[3] = "Query";
for(int i = 0; i < 4; ++i)
mp[ s[i] ] = i;
scanf("%d", &t);
int tot = t;
while(t--)
{
int tem;
scanf("%d", &n);
memset(tree, 0, sizeof(tree));
for(int i = 1; i <= n; ++i)
{
scanf("%d", &tem);
update(i, tem);
}
printf("Case %d:\n", tot - t);
string ch;
while(cin >> ch)
{
bool flag = 0;
int x, y;
switch(mp[ch])
{
case 0:
flag = 1;
break;
case 1:
scanf("%d%d", &x, &y);
update(x, y);
break;
case 2:
scanf("%d%d", &x, &y);
update(x, -y);
break;
case 3:
scanf("%d%d", &x, &y);///注意区间保留x
cout << get_sum(y) - get_sum(x - 1) << '\n';
break;
}
if(flag)
break;
}
}
return 0;
}