线段树操作中的一些预判方法【UOJ 228】【codeforces 438D】

一、UOJ 228

作为一个傻逼题,我……我竟然提交了n次!!!

题意

给出一个长度为 nn 的数列 AA,接下来有 mm 次操作,操作有三种:区间加,区间开方,区间求和。

题解

网上大神们的博客里说,对于一次区间开根:
 设最大值为maxn,最小值为minn,如果maxn-minn=sqrt(maxn)-sqrt(minn),就可以看成区间减法。(因为减小的值是一样的)
 但是,我以为是在一开始判断一次,没想到是在递归中每一次都做判断,所以华丽地TLE了。
 惨啊!感觉要完。

细节见代码。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;

#define N 100010
#define inf 1e16+7
#define lson o << 1
#define rson o << 1 | 1
typedef long long LL;
LL sum[N << 2],add[N << 2],maxn[N << 2],minn[N << 2];
int ll,rr;

void pushup(int o)
{
    sum[o] = sum[lson] + sum[rson];
    maxn[o] = max(maxn[lson],maxn[rson]);
    minn[o] = min(minn[lson],minn[rson]);
}

void build(int o,int l,int r)
{
    if(l == r) {
        scanf("%lld",&sum[o]);
        maxn[o] = minn[o] = sum[o];
        return;
    }
    int mid = (l+r)>>1;
    build(lson,l,mid); build(rson,mid+1,r);
    pushup(o);
}

void pushdown(int o,int l,int r)
{
    if(add[o] != 0) {
        int mid = (l+r)>>1;
        add[lson] += add[o]; maxn[lson] += add[o];minn[lson] += add[o];
        add[rson] += add[o]; maxn[rson] += add[o];minn[rson] += add[o];
        sum[lson] += add[o]*(mid-l+1);
        sum[rson] += add[o]*(r-mid);
        add[o] = 0;
    }
}

void update(int o,int l,int r,LL x)
{
    if(ll <= l && rr >= r) {
        add[o] += x; sum[o] += (r-l+1)*x;
        maxn[o] += x; minn[o] += x;
        return;
    }
    pushdown(o,l,r);
    int mid = (l+r)>>1;
    if(ll <= mid) update(lson,l,mid,x);
    if(rr > mid) update(rson,mid+1,r,x);
    pushup(o);
}

void solve_add(int o,int l,int r,LL x)
{
    add[o] += x; sum[o] += (r-l+1)*x;
    maxn[o] += x; minn[o] += x;
}

void solve(int o,int l,int r)
{
    if(ll <= l && rr >= r)//在这里做特判!!!
    {
        LL a = maxn[o],b = minn[o];
        if((a-b) <= 1 && ((LL)sqrt(a)-(LL)sqrt(b))==(a-b))
        { solve_add(o,l,r,(LL)sqrt(a)-a); return;}
    }
    int mid = (l+r)>>1;
    pushdown(o,l,r);
    if(ll <= mid) solve(lson,l,mid);
    if(rr > mid) solve(rson,mid+1,r);
    pushup(o);
}

LL query(int o,int l,int r)
{
    if(ll <= l && rr >= r) return sum[o];
    int mid = (l+r)>>1;
    pushdown(o,l,r);
    LL ans = 0;
    if(ll <= mid) ans += query(lson,l,mid);
    if(rr > mid) ans += query(rson,mid+1,r);
    return ans;
}

LL ask_max(int o,int l,int r)
{
    if(ll <= l && rr >= r) return maxn[o];
    int mid = (l+r)>>1;
    pushdown(o,l,r);
    LL ans = -1;
    if(ll <= mid) ans = max(ans,ask_max(lson,l,mid));
    if(rr > mid) ans = max(ans,ask_max(rson,mid+1,r));
    return ans;
}

LL ask_min(int o,int l,int r)
{
    if(ll <= r && rr >= r) return minn[o];
    int mid = (l+r)>>1;
    pushdown(o,l,r);
    LL ans = inf;
    if(ll <= mid) ans = min(ans,ask_min(lson,l,mid));
    if(rr > mid) ans = min(ans,ask_min(rson,mid+1,r));
    return ans;
}

int main()
{
    int n,m,opt,x;
    scanf("%d%d",&n,&m);
    memset(add,0,sizeof(add));
    build(1,1,n);
    while(m--) {
        scanf("%d%d%d",&opt,&ll,&rr);
        if(opt == 1) {scanf("%d",&x); update(1,1,n,x);}
        if(opt == 2) solve(1,1,n);
        if(opt == 3) printf("%lld\n",query(1,1,n));
    }
    return 0;
}

二、CF Round#250 div1 D

题意

一个序列,三个操作。
1、区间取模 2、单点修改 3、区间求和

题解

跟上题差不多,就是找一个合适的判断条件(对于每种判断条件还可以求复杂度,但是窝不会-_-)
  多维护一个区间最大值,对于一个符合条件的区间,若最大值小于mod,就可以不进行操作;
  同时,当l==r时,直接就是单点修改。
这样就是一道很普通的线段树了。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

#define lson o << 1
#define rson o << 1 | 1
#define N 100010
typedef long long LL;
int setv[N << 2],maxn[N << 2],mod,ll,rr;
LL sum[N << 2];

void pushup(int o) {
    sum[o] = sum[lson] + sum[rson];
    maxn[o] = max(maxn[lson],maxn[rson]);
}

void pushdown(int o,int l,int r)
{
    if(setv[o] != -1) {
        setv[lson] = setv[rson] = setv[o];
        maxn[lson] = maxn[rson] = setv[o];
        int mid = (l+r)>>1;
        sum[lson] =(LL)(mid-l+1) * setv[o];
        sum[rson] =(LL)(r-mid) * setv[o];
        setv[o] = -1;
    }
}

void build(int o,int l,int r)
{
    setv[o] = -1;
    if(l == r){
        scanf("%lld",&sum[o]);
        maxn[o] = sum[o];
        return;
    }
    int mid = (l+r)>>1;
    build(lson,l,mid); build(rson,mid+1,r);
    pushup(o);
}

void update(int o,int l,int r)
{
    if(ll <= l && rr >= r) {
        if(l == r) { sum[o] %= mod; maxn[o] = sum[o]; return;}
        if(maxn[o] < mod) return;
    }
    int mid = (l+r)>>1;
    pushdown(o,l,r);
    if(ll <= mid) update(lson,l,mid);
    if(rr > mid) update(rson,mid+1,r);
    pushup(o);
}

void change(int o,int l,int r,int pos,int x)
{
    if(l == r){sum[o] = maxn[o] = x; return;}
    int mid = (l+r)>>1;
    pushdown(o,l,r);
    if(pos <= mid) change(lson,l,mid,pos,x); else change(rson,mid+1,r,pos,x);
    pushup(o);
}

LL query(int o,int l,int r)
{
    if(ll <= l && rr >= r) return sum[o];
    int mid = (l+r)>>1;
    LL ans = 0;
    pushdown(o,l,r);
    if(ll <= mid) ans += query(lson,l,mid);
    if(rr > mid) ans += query(rson,mid+1,r);
    pushup(o);
    return ans;
}

int main()
{
    int n,m,opt;
    scanf("%d%d",&n,&m);
    build(1,1,n);
    while(m--) {
        scanf("%d%d%d",&opt,&ll,&rr);
        if(opt == 1) printf("%lld\n",query(1,1,n));
        if(opt == 2) {scanf("%d",&mod); update(1,1,n); }
        if(opt == 3) change(1,1,n,ll,rr);
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值