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

来源:互联网 发布:网络陷阱有哪些 编辑:程序博客网 时间:2024/05/22 14:12

一、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 | 1typedef 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 100010typedef 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;}
1 0
原创粉丝点击