树状数组——人工湖题解

题目:人工湖

描述:

【题目描述】

有一个湖,他的周围都是城市,每个城市都只和他相邻的两个城市有道路相连。假设有n个城市,编号1-n,公路是双向的,公路有时候是好的,有时候是坏的,现在询问你两个城市是否可以互相到达。

【输入格式】

第一行两个数,一个2<=n<=100000  和 1<=m<=100000,分别代表城市数目和询问次数;接下来m行,每一行三个数f,a,b。f=0时,如果公路a,b之间的道路之前是好 的,现在就变成坏的,如果之前是坏的,现在就变成好的。f=1时,询问a,b两个城市是否可以互相到达。

【输出格式】

对于每一个f=1的询问,能到达输出“YES”,否则输出"NO".

【样例输入】

5 10

1 2 5
0 4 5
1 4 5
0 2 3
1 3 4
1 1 3
0 1 2
0 2 3
1 2 4
1 2 5

【样例输出】

YES
YES
YES
NO
YES
NO

【提示】

30%   2<=n,m<=100

50%   2<=n,m<=10000

100%  2<=n,m<=100000

 

  乍一看此题,容易想到可以用搜索来求,但是因为M过大,再加上N的范围也不小,复杂度最大$O(NM)$,所以30分弃疗……但是,树状数组可以很好解决这个问题。将数组开到2*n,下标为1..2*N,第X个单元代表着从X到(X+1) mod 2*N的路径状态,用1表示有,0表示没有。再开一个数组,来存储一些区间中道路情况总和,然后对于每一次改变操作,先进行判断,然后两次改变树状数组中的值。如果是查询操作,则分两次计算:

1. 求x~Y 之间路径总状态,然后判断(注意,如果为‘NO’,不跳出)

2. 求Y~X+N之间的路径总状态,然后判断即可。

  具体实现并没有什么难度,中间添加了一些小技巧而已。但是……F=0时,X,Y的大小不一定是从小到大……害得我全WA了一次……改了一遍才AC……总之总算见到了不是那么果的一道树状数组的题……

AC代码:

{

program zht;
var
i,j,k,m,n,y,x,f,t,z1,z2:longint;
a:array[0..200000] of longint;
c:array[0..200000] of longint;

procedure change;
var
k:longint;
begin
k:=0;
if a[x]=1 then
 begin
  t:=-1;
   a[x]:=0;
   a[x+n]:=0;
   end
 else
  begin
   t:=1;
    a[x]:=1;
     a[x+n]:=1;
     end;                    // 改变状态

k:=x;
 while k<=2*n do
 begin
 c[k]:=c[k]+t;
 k:=k+(k and (-k));
 end;

k:=x+n;

while k<=2*n do
 begin
 c[k]:=c[k]+t;
 k:=k+(k and (-k));
 end;                                  // 树状数组两次改值
end;       // gai bian zhuang tai

procedure chazhao;
var
k:longint;
begin
k:=0;
k:=x-1;
 while k>0 do
 begin
 z1:=z1+c[k];
 k:=k-(k and (-k));
 end;
k:=0;

k:=y-1;

 while k>0 do
 begin
 z2:=z2+c[k];
 k:=k-(k and (-k));
 end;


z1:=z2-z1;


if z1=y-x then begin writeln('YES'); exit; end;        // 直接查找
                                     
z1:=0;
z2:=0;
k:=y-1;
 while k>0 do
  begin
  z1:=z1+c[k];
  k:=k-(k  and (-k));
  end;
k:=x+n-1;
 while k>0 do
  begin
  z2:=z2+c[k];
  k:=k-(k and (-k));
  end;
if z2-z1=n-(y-x) then writeln('YES') else writeln('NO');     // 反向二次查找

end;

begin
assign(input,'lakee.in');
assign(output,'lakee.out');
reset(input);
rewrite(output);

readln(n,m);

for i:=1 to 2*n-1 do
a[i]:=1;

for i:=1 to 2*n-1 do
begin
t:=i and (-i);
 for j:=i-t+1 to i do
  c[i]:=c[i]+1;
end;



for i:=1 to m do
begin
readln(f,x,y);
t:=0;
z1:=0;
z2:=0;

if x>y then begin t:=x; x:=y; y:=t; end;        // 坑点~~~
t:=0;

if f=0 then change
 else chazhao;
end;                        // 分类讨论

close(input);
close(output);
end.                   // 简短的主程序
}

<Marvolo原创,严禁转载>
原文地址:https://www.cnblogs.com/zhtjtcz/p/4997486.html