[均摊 线段树] UOJ #228. 基础数据结构练习题

靠信仰做题

其实很好感性的理解复杂度

想象数列是一排山峰高低不平 可以用O(K*n)次开根把他们削平

区间加操作不过是把其中一段整体抬高 

只要在这段两边多做常数次 loglogn 次就能把他削平

Evan可是用偏导证明的 %%%


#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define mid ((l+r)>>1)
using namespace std;
typedef long long ll;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
  return *p1++;
}

inline void read(int &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

const int N=100005;
struct node{
  ll min,max,sum,add;
  void upd(int l,int r,ll x){
    add+=x,min+=x,max+=x;
    sum+=x*(r-l+1);
  }
}T[N<<2];

inline void Pushup(int x,int l,int r) {
  T[x].min=min(T[x<<1].min,T[x<<1|1].min)+T[x].add;
  T[x].max=max(T[x<<1].max,T[x<<1|1].max)+T[x].add;
  T[x].sum=T[x<<1].sum+T[x<<1|1].sum+T[x].add*(r-l+1);
}

inline void Sqrt(int x,int l,int r,int ql,int qr,ll tag) {
  if(ql<=l && r<=qr) {
    if (T[x].min==T[x].max  || T[x].max-T[x].min==(ll)sqrt(T[x].max+tag)-(ll)sqrt(T[x].min+tag)) {
      ll ad=(ll)sqrt(T[x].min+tag)-T[x].min-tag;
      T[x].upd(l,r,ad);
      return;
    }
    Sqrt(x<<1,l,mid,ql,qr,tag+T[x].add),Sqrt(x<<1|1,mid+1,r,ql,qr,tag+T[x].add);
    Pushup(x,l,r);
    return;
  }
  if(ql<=mid) Sqrt(x<<1,l,mid,ql,qr,tag+T[x].add);
  if(qr>mid) Sqrt(x<<1|1,mid+1,r,ql,qr,tag+T[x].add);
  Pushup(x,l,r);
}

inline void Add(int x,int l,int r,int ql,int qr,ll tag) {
  if(ql<=l&&r<=qr){
    T[x].upd(l,r,tag);
    return;
  }
  if(ql<=mid)
    Add(x<<1,l,mid,ql,qr,tag);
  if(qr>mid)
    Add(x<<1|1,mid+1,r,ql,qr,tag);
  Pushup(x,l,r);
}

inline ll Query(int x,int l,int r,int ql,int qr,ll add) {
  ll ret=0;
  if(ql<=l&&r<=qr)
    return T[x].sum+add*(r-l+1);
  if(ql<=mid)
    ret+=Query(x<<1,l,mid,ql,qr,add+T[x].add);
  if(qr>mid)
    ret+=Query(x<<1|1,mid+1,r,ql,qr,add+T[x].add);
  return ret;
}

int n;
int Val[N];

inline void Build(int x,int l,int r) {
  if(l==r){ T[x].min=T[x].max=T[x].sum=Val[l]; return; }
  Build(x<<1,l,mid),Build(x<<1|1,mid+1,r);
  Pushup(x,l,r);
}

int main(){
  int Q; int order,l,r,t;
  freopen("extra.in","r",stdin);
  freopen("extra.out","w",stdout);
  read(n); read(Q);
  for (int i=1;i<=n;i++) read(Val[i]);
  Build(1,1,n);
  while (Q--){
    read(order); read(l); read(r);
    if (order==1){
      read(t);
      Add(1,1,n,l,r,t);
    }else if (order==2){
      Sqrt(1,1,n,l,r,0);
    }else{
      printf("%lld\n",Query(1,1,n,l,r,0));
    }
  }
  return 0;
}

UPD: 吉丽的证明



就是势能函数 是 相邻两数 的差  最初势能是 O(nV)

每次区间加 会让势能增加 V 分别在两端

对一个满足条件的区间 开根 log log n 次势能下降 V  每次时间复杂度 logn

总势能 (n+m)V 时间复杂度是 n+m logn loglogn


除了替罪羊树以外第一次接触势能分析 好神奇


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值