线段树整理(hdu4578,hdu1698,poj3468,hdu3577)

区间加法、区间乘法、区间替换、区间幂次查询

hdu4578

//
//  main.cpp
//  区间操作集锦_区间加法、区间乘法对查询区间平方和的影响
//
//  Created by 陈冉飞 on 2019/8/14.
//  Copyright © 2019 陈冉飞. All rights reserved.
//

#include <iostream>
using namespace std;
#define mod 10007
#include <cstring>
#define cl(a,b) memset(a,b,sizeof(a))
#define maxn 400050
int flag[maxn],x[maxn];
int n,q,a,b,c,d,i;

void pushup(int k){
    if (flag[k<<1] == 0 || flag[k<<1|1] == 0) flag[k] = 0;
    else if(x[k<<1] != x[k<<1|1]) flag[k] = 0;
    else flag[k] = 1,x[k] = x[k<<1];
}

void pushdown(int k){
    if (flag[k]) {
        x[k<<1] = x[k];
        x[k<<1|1] = x[k];
        flag[k<<1] = flag[k<<1|1] = 1;
        flag[k] = 0;
    }
}

void update(int k,int l,int r,int num,int gl,int gr,int val){
    //区间修改都是要区间覆盖的问题
    if (flag[k] && gl <= l && r <= gr) {
        if (num == 1) x[k] = (x[k] + val)%mod;
        else if(num == 2) x[k] = (x[k]*val)%mod;
        else x[k] = val;
        return;
    }
    pushdown(k);
    int mid = (l+r)>>1;
    if (gl <= mid) update(k<<1, l, mid, num, gl, gr, val);
    if (gr >= mid + 1) update(k<<1|1, mid+1, r, num, gl, gr, val);
    pushup(k);
}

int query(int k,int l,int r,int gl,int gr,int val){
    int f_ans = 0;
    if (flag[k] && gl <= l && r <= gr) {
        int ans = 1;
        //通过循环算次方
        for (i = 1; i <= val; i++) ans = (ans*x[k])%mod;
        ans = (ans*(r-l+1))%mod;
        return ans%mod;
    }
    pushdown(k);
    int mid = (l+r)>>1;
    if (gl <= mid) f_ans += query(k<<1, l, mid, gl, gr, val);
    if (gr >= mid+1) f_ans += query(k<<1|1, mid+1, r, gl, gr, val);
    return f_ans%mod;
}

int main(int argc, const char * argv[]) {
    while (~scanf("%d%d",&n,&q) && n != 0) {
        //先初始化,由于x是储存原值的数组,
        cl(x, 0);cl(flag, 1);
        while (q--) {
            scanf("%d%d%d%d",&a,&b,&c,&d);
            if (a<=3 && a>=1) update(1,1,n,a,b,c,d);
            else printf("%d\n",query(1,1,n,b,c,d));
        }
    }
    return 0;
}

区分区间加法、区间替换

hdu1698

注意这个题是区间都替换成某个值,然后向下pushdown的时候直接将维护的值变成区间长度*变成的值即可。
注意多组数据记得在每次开始的时候初始化数组(忘初始化wa了一发才想起来)

//
//  main.cpp
//  区间改为某个值_hdu1698
//
//  Created by 陈冉飞 on 2019/8/19.
//  Copyright © 2019 陈冉飞. All rights reserved.
//

#include <iostream>
using namespace std;
#include <cstring>
#define cl(a,b) memset(a,b,sizeof(a))
typedef long long ll;
#define maxn 100010
int T,n,q,sum[maxn<<2],c[maxn<<2],tl,tr,tx,kase = 1;

void pushup(int k){
    sum[k] = sum[k<<1]+sum[k<<1|1];
}

void pushdown(int k,int nl,int nr){
    if (c[k]) {
        sum[k<<1] = nl*c[k];
        sum[k<<1|1] = nr*c[k];
        c[k<<1] = c[k<<1|1] = c[k];
        c[k] = 0;
    }
}

void build(int k,int l,int r){
    if (l == r) {sum[k] = 1;return;}
    int mid = (l+r)>>1;
    build(k<<1, l, mid);
    build(k<<1|1, mid+1, r);
    pushup(k);
}

void update(int k,int l,int r,int gl,int gr,int x){
    if (gl <= l && r <= gr) {
        sum[k] = x*(r-l+1);
        c[k] = x;
        return;
    }
    int mid = (l+r)>>1;
    pushdown(k,mid-l+1,r-mid);
    if (gl <= mid) update(k<<1, l, mid, gl, gr, x);
    if (gr >= mid+1) update(k<<1|1, mid+1, r, gl, gr, x);
    pushup(k);
}

int main(int argc, const char * argv[]) {
    for (scanf("%d",&T); T; T--) {
        cl(sum, 0);cl(c, 0);
        scanf("%d%d",&n,&q);
        build(1, 1, n);
        while (q--) {
            scanf("%d%d%d",&tl,&tr,&tx);
            update(1, 1, n, tl, tr, tx);
        }
        printf("Case %d: The total value of the hook is %d.\n",kase++,sum[1]);
    }
    return 0;
}

poj3468

这个在pushdown的时候要将sum加上增加量(也就是区间长度乘上要加的那个值),不能直接替换。

//
//  main.cpp
//  线段树_区间修改、区间求和_poj3468
//
//  Created by 陈冉飞 on 2019/8/13.
//  Copyright © 2019 陈冉飞. All rights reserved.
//

#include <iostream>
//#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define maxn 100050
ll sum[maxn*4],add[maxn*4];
int n,m,i,b,c,d;
char s[10];

void build(int k,int l,int r){
    add[k] = 0; //所有将add标记数组都初始化为零
    if (l == r) {scanf("%lld",&sum[k]);return;}
    int mid = (l+r)>>1;
    build(k<<1, l, mid);
    build(k<<1|1, mid+1, r);
    sum[k] = sum[k<<1]+sum[k<<1|1];
}

void pushdown(int k,int lnum,int rnum){   //传过来的是对下面的区间长度的改变
    if(add[k]){
        sum[k<<1] += (ll)lnum*add[k];   //把左右枝都加上相应的值
        sum[k<<1|1] += (ll)rnum*add[k];
        //把值传到底下
        add[k<<1] += add[k];
        add[k<<1|1] += add[k];
        add[k] = 0;
    }
    return;
}

void change(int k,int l,int r,int gl,int gr,int x){
    //直接区间覆盖即可,此时只要区间包含
    if (gl <= l && r <= gr) {sum[k] += (r-l+1)*x;add[k] += x;return;}
    int mid = (l+r)>>1;
    //在此处有一个向下pushdown的操作,将底下的区间都改变
    pushdown(k, mid-l+1, r-mid);
    if (gl <= mid) change(k<<1, l, mid, gl, gr, x);  //只要gl<=mid,就更改左半段,因为代表左半段有他的部分
    if (gr >= mid+1) change(k<<1|1, mid+1, r, gl, gr, x);
    sum[k] = sum[k<<1]+sum[k<<1|1];
    return;
}

ll query(int k,int l,int r,int gl,int gr){
    if (gl <= l && r <= gr) return sum[k];
    int mid = (l+r)>>1;
    //注意查询的时候也有pushdown的操作
    pushdown(k, mid-l+1, r-mid);
    ll ans = 0;
    if (gl <= mid) ans+= query(k<<1, l, mid, gl, gr);
    if (gr >= mid+1) ans += query(k<<1|1, mid+1, r, gl, gr);
    return ans;
}

int main(int argc, const char * argv[]) {
    scanf("%d%d",&n,&m);
    build(1,1,n);
    while (m--) {
        scanf("%s",s);
        if (s[0] == 'Q'){scanf("%d%d",&b,&c);printf("%lld\n",query(1, 1, n, b, c));}
        else {scanf("%d%d%d",&b,&c,&d);change(1, 1, n, b, c, d);}
    }
    return 0;
}

区间覆盖

hdu3577
区间覆盖问题,就是注意pushup的时候是选最大值。
注意这个储存最后结果的ans向量要记得刷新,(数组刷新了,向量忘记clear。。。)
还有输出格式

//
//  main.cpp
//  区间覆盖、区间查找_hdu3577
//
//  Created by 陈冉飞 on 2019/8/19.
//  Copyright © 2019 陈冉飞. All rights reserved.
//

//#include <iostream>
#include <bits/stdc++.h>
using namespace std;
#define maxn 1000010
int sum[maxn<<2],add[maxn<<2],T,k,q,tl,tr,kase = 1;    //sum为覆盖次数,add为lazy标记
#include <cstring>
#define cl(a,b) memset(a,b,sizeof(a))
#include <vector>
vector<int>ans;

void pushup(int k){
    sum[k] = max(sum[k<<1],sum[k<<1|1]);
}

void pushdown(int k){
    if (add[k]) {
        add[k<<1] += add[k];
        add[k<<1|1] += add[k];
        sum[k<<1] += add[k];
        sum[k<<1|1] += add[k];
        add[k] = 0;
    }
}

int query(int k,int l,int r,int gl,int gr){
    int ret = 0;
    if (gl <= l && r <= gr) return sum[k];
    int mid = (l+r)>>1;
    pushdown(k);
    if (gl <= mid) ret = max(ret,query(k<<1, l, mid, gl, gr));
    if (gr >= mid+1) ret = max(ret, query(k<<1|1, mid+1, r, gl, gr));
    return ret;
}

void update(int k,int l,int r,int gl,int gr){
    if (gl <= l && r <= gr) {
        sum[k] += 1;
        add[k] += 1;
        return;
    }
    pushdown(k);
    int mid = (l+r)>>1;
    if (gl <= mid) update(k<<1, l, mid, gl, gr);
    if (gr >= mid+1) update(k<<1|1, mid+1, r, gl, gr);
    pushup(k);
}

int main(int argc, const char * argv[]) {
    for (scanf("%d",&T); T; T--) {
        cl(sum,0);cl(add,0);ans.clear();
        scanf("%d%d",&k,&q);
        for (int i = 0; i < q; i++) {
            scanf("%d%d",&tl,&tr);
            if (query(1, 1, 1000000, tl, tr-1) < k) {
                ans.push_back(i);
                update(1, 1, 1000000, tl, tr-1);
            }
        }
        printf("Case %d:\n",kase++);
        for (int i = 0; i <ans.size(); i++) cout<<ans[i]+1<<" ";
        cout<<endl<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值