[均摊 平衡树 || 线段树] Codeforces 438D #250 (Div. 1) D. The Child and Sequence

传说中的cf250 china场

题意就是区间取模 区间求和 单点修改

每个数被取模了 肯定会减小一半 一个数最多只要log次就能不动

把相同的数缩成一段

每一段最多被修改O(log n) 次

每一次修改会增加O(1) 段

用平衡树维护 复杂度是 nlog2n 的

对应的势能函数是每一段的log值的和

总势能是(n+m)logn

每次操作log复杂度 能够降1

那么就是两个log的复杂度

等等 这似乎能做这道题加强的区间覆盖版?

是我看错题了 以为是区间覆盖


#include<cstdio>
#include<cstdlib>
#include<algorithm>
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=500005;

struct node{
  int val,cnt,sum,lp,rp; int size; ll ans;
  node *l,*r,*p,*maxv;
  node(){ }
  void newnode(int x,int il,int ir){
    lp=il,rp=ir; size=1; sum=cnt=ir-il+1; val=x; ans=(ll)cnt*val; maxv=this; l=r=p=NULL;
  }
  void setl(node *x) { l=x; if (x) x->p=this; }
  void setr(node *x) { r=x; if (x) x->p=this; }
  void update(){
    maxv=this; sum=cnt; size=1; ans=(ll)cnt*val;
    if (l) { sum+=l->sum,size+=l->size; ans+=l->ans; if (l->maxv->val>maxv->val) maxv=l->maxv; }
    if (r) { sum+=r->sum,size+=r->size; ans+=r->ans; if (r->maxv->val>maxv->val) maxv=r->maxv; }
  }
}nodes[N],*root;
int ncnt;

inline int Size(node *x){ return x?x->size:0; }
inline int Sum(node *x){ return x?x->sum:0; }
inline ll Ans(node *x){ return x?x->ans:0; }

inline int ran(){
  static int x=31253125; x+=(x<<4)+1; return x&65536;
}

inline node* Merge(node *A,node *B){
  if (!A || !B) return A?A:B;
  if (ran()){
    node *y=Merge(A->r,B);
    A->setr(y); A->update();
    return A;
  }else{
    node *y=Merge(A,B->l);
    B->setl(y); B->update();
    return B;
  }
}

typedef pair<node*,node* > Droot;

inline Droot Split(node *x,int k){
  if (!x) return Droot(NULL,NULL);
  Droot y;
  if(Size(x->l)>=k){
    y=Split(x->l,k);
    x->setl(y.second); x->update();
    if (y.first) y.first->p=NULL;
    y.second=x;
  }else{
    y=Split(x->r,k-Size(x->l)-1);
    x->setr(y.first); x->update();
    if (y.second) y.second->p=NULL;
    y.first=x;
  }
  return y;
}

inline int Find(int k){
  node *x=root; int ret=0;
  while (1){
    if (k>Sum(x->l) && k<=Sum(x->l)+x->cnt)
      return ret+Size(x->l)+1;
    if (Sum(x->l)>=k)
      x=x->l;
    else
      k-=Sum(x->l)+x->cnt,ret+=1+Size(x->l),x=x->r;
  }
}

inline node *Findkth(int k){
  node *x=root;
  while (1){
    if (k==Size(x->l)+1) return x;
    Size(x->l)>=k?x=x->l:(k-=Size(x->l)+1,x=x->r);
  }
}

inline node *Build(int *a,int l,int r){
  if (l>r) return NULL;
  if (l==r) {
    nodes[++ncnt].newnode(a[l],l,r);
    return nodes+ncnt;
  }
  int mid=(l+r)>>1,t=++ncnt;
  nodes[t].newnode(a[mid],mid,mid);
  nodes[t].setl(Build(a,l,mid-1)); nodes[t].setr(Build(a,mid+1,r));
  nodes[t].update();
  return nodes+t;
}

int n,a[N];

inline void Work(int l,int r){
  int lp=Find(l),rp=Find(r);
  if (lp==rp){
    Droot x=Split(root,lp-1);
    Droot y=Split(x.second,1);
    node *t=y.first,*a=NULL,*b=NULL;
    if (t->lp<=l-1) nodes[++ncnt].newnode(t->val,t->lp,l-1),a=nodes+ncnt;
    if (r+1<=t->rp) nodes[++ncnt].newnode(t->val,r+1,t->rp),b=nodes+ncnt;
    t->newnode(t->val,l,r);
    t=Merge(a,Merge(t,b));
    root=Merge(x.first,Merge(t,y.second));
    return;
  }
  Droot x=Split(root,lp-1);
  Droot y=Split(x.second,1);
  Droot z=Split(y.second,rp-lp-1);
  Droot w=Split(z.second,1);
  node *t=y.first,*h=w.first,*a=NULL,*b=NULL;
  if (t->lp<=l-1) nodes[++ncnt].newnode(t->val,t->lp,l-1),a=nodes+ncnt;
  t->newnode(t->val,l,t->rp); t=Merge(a,t);
  if (r+1<=h->rp) nodes[++ncnt].newnode(h->val,r+1,h->rp),b=nodes+ncnt;
  h->newnode(h->val,h->lp,r); h=Merge(h,b);
  root=Merge(x.first,Merge(t,Merge(z.first,Merge(h,w.second))));
}

int main(){
  int Q,order,l,r,w;
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  read(n); read(Q);
  for (int i=1;i<=n;i++) read(a[i]);
  root=Build(a,1,n);
  while (Q--){
    read(order);
    if (order==1){
      read(l); read(r);
      Work(l,r);
      int lp=Find(l),rp=Find(r);
      Droot x=Split(root,lp-1);
      Droot y=Split(x.second,rp-lp+1);
      printf("%I64d\n",Ans(y.first));
      root=Merge(x.first,Merge(y.first,y.second));
    }else if (order==2){
      read(l); read(r); read(w);
      Work(l,r);
      int lp=Find(l),rp=Find(r);
      Droot x=Split(root,lp-1);
      Droot y=Split(x.second,rp-lp+1);
      node *t=y.first,*f;
      while ((f=t->maxv)->val>=w){
	f->val%=w;
	while (f!=t) f->update(),f=f->p; t->update();
      }
      root=Merge(x.first,Merge(y.first,y.second));
    }else if (order==3){
      read(l); r=l; read(w);
      Work(l,r);
      int lp=Find(l),rp=Find(r);
      Droot x=Split(root,lp-1);
      Droot y=Split(x.second,rp-lp+1);
      y.first->val=w; y.first->update();
      root=Merge(x.first,Merge(y.first,y.second));
    }
  }
  return 0;
}



实际只需要一个线段树 每次找最大的数能%就% 不能就break好了

#include<cstdio>
#include<cstdlib>
#include<algorithm>
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;

int n,a[N];

inline int Max(int x,int y){
  return a[x]>a[y]?x:y;
}

struct Seg{
  int M;
  int T[N<<2]; ll S[N<<2];
  void Build(int n,int *a){
    for (M=1;M<n+2;M<<=1);
    for (int i=1;i<=n;i++)
      T[M+i]=i,S[M+i]=a[i];
    for (int i=M-1;i;i--)
      T[i]=Max(T[i<<1],T[i<<1|1]),S[i]=S[i<<1]+S[i<<1|1];
  }
  void Modify(int s,int r){
    S[s+=M]=r; T[s]=s-M;
    while (s>>=1)
      T[s]=Max(T[s<<1],T[s<<1|1]),S[s]=S[s<<1]+S[s<<1|1];
  }
  int Maxv(int s,int t){
    ll ret=0;
    for (s+=M-1,t+=M+1;s^t^1;s>>=1,t>>=1){
      if (~s&1) ret=Max(ret,T[s^1]);
      if ( t&1) ret=Max(ret,T[t^1]);
    }
    return ret;
  }
  ll Sum(int s,int t){
    ll ret=0;
    for (s+=M-1,t+=M+1;s^t^1;s>>=1,t>>=1){
      if (~s&1) ret+=S[s^1];
      if ( t&1) ret+=S[t^1];
    }
    return ret;
  }
}Seg;

int main(){
  int Q,order,l,r,w;
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  read(n); read(Q);
  for (int i=1;i<=n;i++) read(a[i]);
  Seg.Build(n,a);
  while (Q--){
    read(order);
    if (order==1){
      read(l); read(r);
      printf("%I64d\n",Seg.Sum(l,r));
    }else if (order==2){
      read(l); read(r); read(w);
      int t;
      while (a[t=Seg.Maxv(l,r)]>=w){
	a[t]%=w;
	Seg.Modify(t,a[t]);
      }
    }else if (order==3){
      read(l); read(w);
      a[l]=w;
      Seg.Modify(l,w);
    }
  }
  return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值