zkw线段树 运用

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神牛。。。

var d:array[1..2,1..262144]of int64; a:array[0..100000]of int64; n,m,m1:longint; procedure change(k,x:longint;y:int64); begin x:=x+m1; while x<>0 do begin d[k,x]:=d[k,x]+y;x:=x>>1 end end; function ask(k,l,r:longint):int64; begin l:=l+m1-1;r:=r+m1+1;ask:=0; while not(l xor r=1) do begin if l and 1=0 then ask:=ask+d[k,l+1]; if r and 1=1 then ask:=ask+d[k,r-1]; l:=l>>1;r:=r>>1 end end; procedure origin; begin fillchar(d,sizeof(d),0); m1:=1; while m1<=n+2 do m1:=m1<<1;m1:=m1-1 end; procedure init; var l,r,ans1,ans2,x:int64; i:longint; z1,z2:char; begin readln(n,m); origin; for i:=1 to n do read(a[i]); for i:=1 to n do begin change(1,i,a[i]-a[i-1]);change(2,i,i*(a[i]-a[i-1])) end; readln; for i:=1 to m do begin read(z1,z2,l,r); if z1='Q' then begin l:=l-1; ans1:=ask(1,1,r)*(r+1);ans1:=ans1-ask(2,1,r); if l<>0 then begin ans2:=ask(1,1,l)*(l+1);ans2:=ans2-ask(2,1,l); end else ans2:=0; writeln(ans1-ans2) end else begin read(x); change(1,l,x);if r<n then change(1,r+1,-x); change(2,l,l*x);if r<n then change(2,r+1,-(r+1)*x) end; readln end end; begin init end. 

写了个自顶向下的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;
}


你可能感兴趣的:(c,Build)