树状数组——人工湖题解

题目:人工湖

描述:

【题目描述】

有一个湖,他的周围都是城市,每个城市都只和他相邻的两个城市有道路相连。假设有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原创,严禁转载>
时间: 2024-10-10 15:45:39

树状数组——人工湖题解的相关文章

洛谷 P3374 【模板】树状数组 1 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:https://www.luogu.org/problem/show?pid=3374 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某一个数加上x 2.求出某区间每一个数的和 输入输出格式 输入格式: 第一行包含两个整数N.M,分别表示该数列数字的个数和操作的总个数. 第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值. 接下来M行每行包含3或4个整数,表示一个操作,具体如下: 操

luoguP2184 贪婪大陆 题解(树状数组)

P2184 贪婪大陆  题目 其实很容易理解就是询问一段区间内有多少段不同的区间 然后再仔细思索一下会发现: 1.只要一个区间的开头在一个节点i的左边,那么这个区间包含在区间1~i中. 2.只要一个区间的尾部在一个节点j的左边,那么这个区间肯定不属于j之后的所有区间 这时候就不难想到用两个树状数组维护: 第一个:维护节点i之前有多少个区间的开头 第二个:维护节点j之前有多少个区间的结尾 不难证明拿sum[i]-sum[j]得到的就是i~j中间地雷的个数(手动模拟一波就一清二楚了) #includ

【题解】Leyni,罗莉和队列(树状数组)

[题解]Leyni,罗莉和队列(树状数组) HRBUST - 1356 将整个序列reverse一下,现在就变成了从高到低的排队.题目就变成了,定位一个妹子,问这个妹子前面的比这个妹子小的妹子中,下标最小的数是哪个. 所以以年龄为下标,以(原数组)下标为值建立一颗值域线段树,由于我们只要找到在原数组下标最小的哪一个,所以相当于动态维护前缀\(\min\) ,由于我们只要查询比当前定位妹子小的一段前缀,所以拿树状数组代替就是了. 由于这群妹子是神仙(年龄可达1e9),所以要离散化一下. 复杂度\(

【题解】[CJOI2019] 树上查询(树状数组)

[题解][CJOI2019] 树上查询(树状数组) 题目描述 班?小 A 需要管理信息组的日常纪律.所有人都在树形机房学习,树形机房的根节点为 1 .信息组的同学很多,但树 形机房的每个节点上有且仅有一个同学.小 A 的位置在树形机房的某个节点上,他想要管理在他所站节点子树中的同学(不算自己).每个位置都有一个管理容易值\(w_i\) ,他能管理到某个位置 ,当且仅当他们的距离不超过\(w_i\). 定位一个根\(x\),现在就是求\(u\in S\),\(S\)是\(x\)的所有子树所有节点集

POJ2299题解(树状数组)

POJ2299题解 2019-12-28 Powered by Gauss 1.题目传送门:POJ2299 2.理解题意: 做题的时候,第一步也是最重要的就是理解题意.记住这句话. POJ2299是一道标准的树状数组模板题.题意大致如下: 快速排序是一种非常优秀的排序方式,其精华在于循环比较和swap函数的应用. 先给你一个数N,表示这组数据(没错,这题是多组数据)数的个数,之后的N行,每行一个数Ai 请你输出在快速排序过程中经历的swap函数的次数. 3.算法思路: 树状数组是一种非常高效的数

POJ2352题解(树状数组)

POJ2352题解(树状数组) 2019-12-29 Powered by Gauss 1.题目传送门:POJ2352 2.题目大意: 这是一道非常经典的树状数组的模板题. 题目大意是说,给出N颗星星,每个星星都有一个二维坐标,要求出位于每颗星星左下方的星星的数量. 3.算法思路: 这道题被给出之后立刻想到暴力法,让我们来计算一下: 根据题目,N<=15000,暴力法需要双层循环,也就是150002=225,000,000,超出了一秒,所以不得不寻求更快更好的算法思想. 当我们需要求查询,求和的

PAT甲题题解-1057. Stack (30)-树状数组

不懂树状数组的童鞋,正好可以通过这道题学习一下树状数组~~百度有很多教程的,我就不赘述了 题意:有三种操作,分别是1.Push key:将key压入stack2.Pop:将栈顶元素取出栈3.PeekMedian:返回stack中第(n+1)/2个小的数 建立一个栈来模拟push和pop,另外还需要树状数组,来统计栈中<=某个数的总个数不了解树状数组的建议学习一下,很有用的.树状数组为c,有个虚拟的a数组,a[i]表示i出现的次数sum(i)就是统计a[1]~a[i]的和,即1~i出现的次数当我要

PAT甲题题解-1095. Cars on Campus(30)-(map+树状数组,或者模拟)

题意:给出n个车辆进出校园的记录,以及k个时间点,让你回答每个时间点校园内的车辆数,最后输出在校园内停留的总时间最长的车牌号和停留时间,如果不止一个,车牌号按字典序输出. 几个注意点: 1.如果一个车连续多次进入,只取最后一个 2.如果一个车连续多次出去,只取第一个 3.一个车可能出入校园内好几次,停留时间取总和 实际上题目就是让我们求某个时间段内的车辆总和,时间段其实就相当于一个区间,区间求和的话,很快就联想到树状数组和线段树.然而怎么将时间段和区间联系起来呢,那就存储出现在记录和询问里的所有

[CTSC2017]最长上升自序列(伪题解)(树状数组+DP套DP+最小费用最大流+Johnson最短路+Yang_Tableau)

部分分做法很多,但每想出来一个也就多5-10分.正解还不会,下面是各种部分分做法: Subtask 1:k=1 LCS长度最长为1,也就是说不存在j>i和a[j]>a[i]同时成立.显然就是一个LDS,树状数组直接求即可. Subtask 2:k=2 最多两个,也就是可以由两个LCS拼起来,f[i][j]表示第一个LCS以i结尾,第二个以j结尾的方案数,转移显然. Subtask 3:k=2 树状数组优化DP,复杂度由$O(n^3)$降为$O(n^2 \log n)$ Subtask 4,5: