现在的位置: 首页 > 综合 > 正文

zkw线段树 运用

2014年03月04日 ⁄ 综合 ⁄ 共 3519字 ⁄ 字号 评论关闭

poj 3468

经典的区间修改和区间和查询,需要灵活运用差分和前缀和。

设A为原数组,A'为差分数组(A'i:=Ai-A[I-1])。

那么,我们如果要求Ai,便可从A'1+...A'I得出,由此看来A=SA'(前缀和),

于是,我们想要快速知道Ai~j的和方法之一便是SAj-SAi,由上式可知,SA=SSA'(前缀和的前缀和)

SSA'i=SA’1+...SA'i=i*A‘1+(I-1)*A’2...=(i+1)*(A‘1+...A’i)-(1*A‘1+2*A’2+...I*A‘i)

我们为什么要把简单式子弄这么复杂,原因只有有一个——方便维护。

线段树支持区间修改加点查询或点修改加区间查询,但难以实现区间修改加区间查询,想不变splay之类的东西的话,就要充分利用转化思想,

上式的性质很优美,当你在[i,j]加上v时,由于差分,除A’I,A‘[J+1]外,其他点都不需要修改,每次查询时可以很方便求区间和再利用上式出解。

我们只需维护两个数列的线段树,A'、{i*A'I}。利用差分性质讨论,可以得出单点如何修改维护,实在不懂可以看我代码,或直接提问。

多说一句,我前面一个竟然就是zkw神牛。。。

 

写了个自顶向下的splay版

#include <cstdio>
#include <cstdlib>
#include <cstring>
long long t[105000],sum[105000],ans,a[105000],size[105000];
int l[105000],r[105000],ll,rr,lr,rl,rs[105000],ls[105000],n,k,mid;
void pushdown(int x) 
{ if (0==x) return ; 
  if (l[x]) sum[l[x]]=sum[l[x]]+t[x]*size[l[x]],t[l[x]]=t[l[x]]+t[x],a[l[x]]=a[l[x]]+t[x];
  if (r[x]) sum[r[x]]=sum[r[x]]+t[x]*size[r[x]],t[r[x]]=t[r[x]]+t[x],a[r[x]]=a[r[x]]+t[x];
  t[x]=0;
}
void updata(int x) {size[x]=size[l[x]]+size[r[x]]+1,sum[x]=sum[l[x]]+sum[r[x]]+a[x];}
int left(int x,int y) {pushdown(x),r[y]=l[x],l[x]=y,updata(y);return x;}
int right(int x,int y){pushdown(x),l[y]=r[x],r[x]=y,updata(y);return x;}
void lconnect(int x)
{
  if (0==x) return ;
  if (0==ll) {ll=lr=x,ls[ls[0]=1]=x;return ;}
  r[lr]=x,ls[++ls[0]]=lr=x;
}
void rconnect(int x)
{
  if (0==x) return ;
  if (0==rr) {rr=rl=x,rs[rs[0]=1]=x;return ;}
  l[rl]=x,rs[++rs[0]]=rl=x;
}
void splay(int &mid,int x)
{
  ll=rr=lr=rl=rs[0]=ls[0]=0;
  int y,i;
  for (;(mid!=x);)
  {
    pushdown(mid);
    if (x<mid) {
        if ((x<l[mid])&&(l[mid])) mid=right(l[mid],mid);
        rconnect(mid),y=l[mid],l[mid]=0,mid=y;
    }
    else {
        if ((r[mid]<x)&&(r[mid])) mid=left(r[mid],mid);
        lconnect(mid),y=r[mid],r[mid]=0,mid=y;
    }
  }
  pushdown(mid),pushdown(l[mid]),pushdown(r[mid]);
  if (l[mid]) lconnect(l[mid]),ls[0]--;
  if (r[mid]) rconnect(r[mid]),rs[0]--;
  for (i=ls[0];i>=1;i--) y=ls[i],updata(y);
  for (i=rs[0];i>=1;i--) y=rs[i],updata(y);
  l[mid]=ll,r[mid]=rr,updata(mid);
}
int build(int ll,int rr)
{
  int mid=(ll+rr)>>1;
  if (mid+1<=rr) r[mid]=build(mid+1,rr);
  if (ll<=mid-1) l[mid]=build(ll,mid-1);
  updata(mid);  
  return mid;
}
void init()
{
  int i,al,ar;
  char ch;
  long long ak;
  scanf("%d%d\n",&n,&k);n+=2;
  for (i=2;i<=n-1;i++) scanf("%I64d",&a[i]);scanf("\n");
  mid=(1+n)>>1,l[mid]=build(1,mid-1),r[mid]=build(mid+1,n),updata(mid);
  for (i=1;i<=k;i++)
  {
    scanf("%c",&ch);
    if ('Q'==ch){
        scanf("%d%d\n",&al,&ar);al++,ar++;
        splay(mid,ar+1),splay(l[mid],al-1);
        ans=sum[r[l[mid]]];
        printf("%I64d\n",ans);
    }
    else {
        scanf("%d%d%I64d\n",&al,&ar,&ak);al++,ar++;
        splay(mid,ar+1),splay(l[mid],al-1);
        al=r[l[mid]];
        t[al]=t[al]+ak,sum[al]=sum[al]+ak*size[al],a[al]=a[al]+ak;
        updata(l[mid]),updata(mid);
    }
  }
}
int main()
{
  freopen("simple.in","r",stdin);
  freopen("simple.out","w",stdout);
    init();
  return 0;
}

抱歉!评论已关闭.