数据结构(线段树+树状数组)合集

线段树:

1、【codevs1690】开关灯

这道题是一道线段树裸题,可以开个标记记录当前节点被修改的次数,然后仿照区间修改区间查询来做就行了。

代码:

uses math;
type hh=record
  l,r,x,sum:longint;
end;
var a:array[0..400010]of hh;
  n,m,i,x,l,r:longint;
function calc(now:longint):longint;
begin
  exit(a[now].r-a[now].l+1);
end;
procedure build(now,l,r:longint);
begin
  a[now].l:=l; a[now].r:=r; a[now].x:=0; a[now].sum:=0;
  if l<>r then begin
    build(now<<1,l,(l+r)>>1); build(now<<1 or 1,(l+r)>>1+1,r);
  end;
end;
procedure change(now,l,r:longint);
begin
  if(l<=a[now].l)and(a[now].r<=r)then begin
    a[now].x:=a[now].x xor 1; a[now].sum:=calc(now)-a[now].sum;
  end
  else begin
    if l<a[now<<1 or 1].l then change(now<<1,l,r);
    if a[now<<1].r<r then change(now<<1 or 1,l,r);
    a[now].sum:=a[now<<1].sum+a[now<<1 or 1].sum;
    if a[now].x=1 then a[now].sum:=calc(now)-a[now].sum;
  end;
end;
function query(now,l,r:longint):longint;
var ret:longint;
begin                                                                     
  if(l=a[now].l)and(a[now].r=r)then exit(a[now].sum)
  else begin
    ret:=0;
    if l<a[now<<1 or 1].l then ret:=ret+query(now<<1,l,min(r,a[now<<1].r));
    if a[now<<1].r<r then ret:=ret+query(now<<1 or 1,max(l,a[now<<1 or 1].l),r);
    if a[now].x=0 then exit(ret)
    else exit(r-l+1-ret);
  end;
end;
begin
  read(n,m);
  build(1,1,n);
  for i:=1 to m do begin
    read(x,l,r);
    if x=0 then change(1,l,r)
    else writeln(query(1,l,r));
  end;
end.
View Code

(虽然是一道裸题,但我还是WA了好几天)

2、【codevs1299】切水果

这道题是典型的区间set+区间查询,不过因为是全区间查询,所以可以直接输出根节点的sum(Hqy神犇这道写了一个25行线段树,%%%)

代码:

type tree=record
  l,r,sum:longint;
  en:boolean;
end;
var a:array[0..2000100]of tree;
  n,m,i,l,r:longint;
procedure build(now,l,r:longint);
begin
  a[now].l:=l; a[now].r:=r; a[now].sum:=r-l+1; a[now].en:=false;
  if l<>r then begin
    build(now<<1,l,(l+r)>>1); build(now<<1 or 1,(l+r)>>1+1,r);
  end;
end;
procedure cut(now,l,r:longint);
begin 
  if a[now].en then exit;
  if(l<=a[now].l)and(a[now].r<=r)then begin
    a[now].en:=true; a[now].sum:=0; exit;
  end;
  if l<a[now<<1 or 1].l then cut(now<<1,l,r);
  if a[now<<1].r<r then cut(now<<1 or 1,l,r);
  a[now].sum:=a[now<<1].sum+a[now<<1 or 1].sum;
  if(a[now<<1].en)and(a[now<<1 or 1].en)then a[now].en:=true;
end;
begin
  read(n,m);
  build(1,1,n);
  for i:=1 to m do begin
    read(l,r);
    cut(1,l,r);
    writeln(a[1].sum);
  end;
end.
View Code

==========================分割线================================

树状数组:

1、【codevs1228】苹果树

这道题乍一看以为是树剖,蓝鹅我不会……

于是看了看题解,看到了一种神奇的思路:

因为是单点修改+子树查询,所以我们就能利用树的dfs的序的性质:一颗子树的所有节点一定会在dfs序中挤在一坨。

所以我们就能求出dfs序,记录每个节点在dfs序中的位置和以它为根的子树所代表的区间,然后就能愉快地单点修改+区间查询了。

代码:

var a,ne:array[0..200010]of longint;
  h,x,y,q,c,cou:array[0..100010]of longint;
  b:array[0..100010]of boolean;
  n,m,i,k,u,v,num:longint;
  ch:char;
procedure add(x,y:longint);
begin
  inc(m); a[m]:=y; ne[m]:=h[x]; h[x]:=m;
end;
procedure dfs(now:longint);
var i:longint;
begin
  inc(m); q[now]:=m; x[now]:=m; b[now]:=false;
  i:=h[now];
  while i>0 do begin
    if b[a[i]] then dfs(a[i]);
    i:=ne[i];
  end;
  y[now]:=m;
end;
function lowbit(x:longint):longint;
begin
  exit(x and -x);
end;
procedure change(x,num:longint);
begin
  while x<=n do begin
    c[x]:=c[x]+num;
    x:=x+lowbit(x);
  end;
end;
function sum(x:longint):longint;
begin
  sum:=0;
  while x>0 do begin
    sum:=sum+c[x];
    x:=x-lowbit(x);
  end;
end;
begin
  read(n); m:=0;
  fillchar(h,sizeof(h),255);
  for i:=1 to n-1 do begin
    read(u,v);
    add(u,v); add(v,u);
  end;
  fillchar(b,sizeof(b),true);
  m:=0;
  dfs(1);
  fillchar(c,sizeof(c),0);
  for i:=1 to n do begin
    cou[i]:=1; change(i,1);
  end;
  readln(m);
  for i:=1 to m do begin
    readln(ch,k);
    if ch='Q' then writeln(sum(y[k])-sum(x[k]-1))
    else begin
      cou[q[k]]:=-cou[q[k]];
      change(q[k],cou[q[k]]);
    end;
  end;
end.
View Code

 =======================算了不更了===============================

原文地址:https://www.cnblogs.com/quzhizhou/p/6534580.html