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

时间:2022-07-03 20:05:51

一、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;
}