线段树:
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.
(虽然是一道裸题,但我还是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.
==========================分割线================================
树状数组:
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.
=======================算了不更了===============================