引入:树状数组
自我感悟
1.树状数组奇数为第0阶梯,偶数却不为2的m次方的形式为第1阶梯,偶数为2的m次方的形式为第m阶梯,
2.可以根据目的对阶梯性质进行定义,如:和,最大值...,
3.阶梯的覆盖优先顺序为右方高阶梯覆盖左方低阶梯,且每个结点只有一个父结点,
4.低阶梯到高阶梯的方式为+x&(-x)
A - 敌兵布阵
线段树模板题:执行操作单点加减,区间求和
#include
using namespace std;
typedef long long ll;
const int N = 5e4 + 10;
int w[N];
struct node {
int l, r;
int sum;
}t[N << 2];
void pushup(int x) {
t[x].sum = t[x << 1].sum + t[x << 1 | 1].sum;
}
void build(int l, int r, int x = 1) {
t[x] = { l, r, w[l] };
if (l == r) return;
int mid = l + r >> 1; //x|1表示奇数则本身,偶数则+1
build(l, mid, x << 1), build(mid + 1, r, x << 1 | 1); //按位或运算符则是将两个数字的二进制值的每一位进行或运算。
pushup(x);
}
void modify(int a, int c, int x = 1) {
if (a == t[x].l && a == t[x].r) { t[x].sum += c; return; }
int mid = t[x].l + t[x].r >> 1;
modify(a, c, x << 1 | (a > mid)); //a>mid为1,奇数则本身,偶数则+1,a= t[x].r) return t[x].sum; //囊括全部
int mid = t[x].l + t[x].r >> 1;
int res = 0;
if (l <= mid) res += ask(l, r, x << 1);
if (r > mid) res += ask(l, r, x << 1 | 1);
return res;
}
int main()
{
int t; cin >> t;
for (int T = 1; T <= t;T++ ) {
printf("Case %d:\n", T);
int n;
scanf("%d", &n);
for(int i=1;i<=n;i++)
scanf("%d", &w[i]);
build(1, n);
char s[10];
while (scanf("%s", s)) {
if (s[0] == 'E')
break;
int x, y;
scanf("%d %d", &x, &y);
if (s[0] == 'A')
modify(x, y);
else if (s[0] == 'S')
modify(x, -y);
else if (s[0] == 'Q')
printf("%d\n", ask(x, y));
}
}
return 0;
}
树状数组解法
#include
using namespace std;
typedef long long ll;
const int N = 5e4 + 10;
int t[N]; //树状数组
int n, m;
int lowbit(int x) {
return x & -x;
}
void add(int x, int c) {
for (int i = x; i <= n; i += lowbit(i)) //举例:以1开始,1,2,4,8..;2开始,2,4,8
t[i] += c;
}
int ask(int x) { //举例:7->6->4
int res = 0;
for (int i = x; i; i -= lowbit(i))
res += t[i];
return res;
}
int main()
{
int tt;
cin >> tt;
for (int T = 1; T <= tt;T++) {
printf("Case %d:\n", T);
scanf("%d", &n);
for(int i=1;i<=n;i++)
t[i] = 0; //初始化
for (int i = 1; i <= n; i++) {
int x;
scanf("%d", &x);
add(i, x);
}
while (true) {
char s[10]; scanf("%s", s);
if (s[0] == 'E')
break;
int a, b;
scanf("%d %d", &a, &b);
if (s[0] == 'A')
add(a, b);
else if (s[0] == 'S')
add(a, -b);
else if (s[0] == 'Q')
printf("%d\n", ask(b) - ask(a - 1));
}
}
return 0;
}
B - I Hate It
思路:树状数组
执行操作:单点修改,区间求最大值
a树状数组记录原素组,b树状数组记录最大值,单点修改,区间求最大值自需向高阶梯递进,枚举低阶梯即可
#include
#include
#include
#define Z 200005
using namespace std;
int n, m, op1, op2;
char ch[5];
int c[Z], a[Z];
int lowbit(int x){
return x & (-x);
}
int geta(int l, int r){
int temp;
int ans = 0;
while (r >= l){
ans = max(a[r], ans);
r--;
for (; r - lowbit(r) >= l; r -= lowbit(r)){ //向低阶梯
ans = max(ans, c[r]);
}
}
return ans;
}
void update(int x){ //更新最大值
int temp;
while (x <= n){
temp = lowbit(x); //奇数1,0阶梯,不需比较
c[x] = a[x];
for (int i = 1; i < temp; i <<= 1) //假设temp:8,则枚举了7,6,4,三个高阶梯
c[x] = max(c[x], c[x - i]);
x += temp; //向高阶梯
}
return;
}
void up(int x, int i){ //每读入一个数,原来的最大值不变,或者最大值变为a[i],没必要枚举下面子节点
int temp;
while (x <= n && a[i] > c[x]){
temp = lowbit(x);
c[x] = a[i];
x += temp;
}
return;
}
int main(){
while (scanf("%d%d", &n, &m) != EOF){
memset(c, 0, sizeof(c)); //记录区间最大值
for (int i = 1; i <= n; i++){
scanf("%d", &a[i]); //原数组
up(i, i);
}
for (int i = 1; i <= m; i++){
scanf("%s%d%d", ch, &op1, &op2);
if (ch[0] == 'Q')
printf("%d\n", geta(op1, op2));
else{
a[op1] = op2;
update(op1);
}
}
}
return 0;
}
C - A Simple Problem with Integers
执行操作:区间加减,区间求和
在区间内前缀和增加x*(i-l+1)=x*i-x*(l-1),维护前缀和树状数组和加法树状数组即可,
通过加减实现在[l,r]实现维护,最后再两个数组在[l.r]相加即可
#include
#include
using namespace std;
typedef long long ll;
#define MAX 100005
int n, q;
int a[MAX];
ll bit0[MAX], bit1[MAX]; //定义bit0前缀和树状数组,bit1加法树状数组
void updata(ll* b, int i, int val){
while (i <= n){
b[i] += val;
i += (i & -i); //向高阶梯
}
}
ll query(ll* b, int i){
ll res = 0;
while (i > 0){
res += b[i];
i -= (i & -i); //向低阶梯
}
return res;
}
int main()
{
ios_base::sync_with_stdio(0);
while (cin >> n >> q){
memset(bit0, 0, sizeof(bit0));
memset(bit1, 0, sizeof(bit1));
for (int i = 1; i <= n; i++){
cin >> a[i];
updata(bit0, i, a[i]);
}
while (q--){
int l, r, x;
char ch;
cin >> ch;
cin >> l >> r;
if (ch == 'C'){ //在[l,r]进行操作
cin >> x;
updata(bit0, l, -x * (l - 1));
updata(bit1, l, x);
updata(bit0, r + 1, x * r);
updata(bit1, r + 1, -x);
}
else{ //在[l,r]进行操作
ll sum = 0;
sum += query(bit0, r) + query(bit1, r) * r;
sum -= query(bit0, l - 1) + query(bit1, l - 1) * (l - 1);
cout << sum << endl;
}
}
}
}
D-just a Hook
引入:懒标记
操作:区间修改,输出[1,n]和
#include
#include
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
struct node {
int l, r;
int val; //区间和
int lazy; //懒标记
}t[N << 2];
void pushdown(node& op, int lazy) {
op.val = lazy * (op.r - op.l + 1);
op.lazy = lazy;
}
void Pushdown(int x) {
if (!t[x].lazy) return;
pushdown(t[x << 1], t[x].lazy);
pushdown(t[x << 1 | 1], t[x].lazy);
t[x].lazy = 0;
}
void pushup(int x) {
t[x].val = t[x << 1].val + t[x << 1 | 1].val;
}
void build(int l, int r, int x = 1) {
t[x] = { l, r, 1, 0 };
if (l == r) return;
int mid = l + r >> 1;
build(l, mid, x << 1), build(mid + 1, r, x << 1 | 1);
pushup(x);
}
void modify(int l, int r, int c, int x = 1) {
if (l <= t[x].l && r >= t[x].r) {
pushdown(t[x], c);
return;
}
Pushdown(x);
int mid = t[x].l + t[x].r >> 1;
if (l <= mid) modify(l, r, c, x << 1);
if (r > mid) modify(l, r, c, x << 1 | 1);
pushup(x);
}
int main()
{
int T;
cin >> T;
for(int i=1;i<=T;i++) {
int n, m;
scanf("%d%d", &n, &m);
build(1, n);
while (m--) {
int l, r, c;
scanf("%d%d%d", &l, &r, &c);
modify(l, r, c);
}
printf("Case %d: The total value of the hook is %d.\n", i, t[1].val);
}
return 0;
}
java(5)