CF983E NN country [倍增][LCA][树状数组]

题意:

  $n$个城市,从$1$到$n$标号,$n$个城市构成一棵树。

  有$m$条双向公交路线,对于每条路线,公交沿着两个终点站之间的最短路径行驶并会在沿途各站停车。从一个城市只能坐公交前往其他城市。

  有$q$个询问:从一个城市到另一个城市要搭乘多少趟公交?不能到达输出$-1$。



对于每个询问$x,y$,求出$z=lca(x,y)$。

先从$x$和$y$出发到达$z$下方的城市$x‘$和$y‘$使得再坐一趟车可到$z$,记步数和为$s$。倍增,预处理$f[i][j]$表示从$i$出发坐$2^{j}$趟车最上能到达的位置。

若$x‘$可坐车到$y‘$则答案为$s+1$,否则答案为$s+2$。

考虑dfs过程中用树状数组按dfs序维护,若$x‘$的子树中终点站在$y‘$子树内的车,那么可以坐一趟车到达。预处理时把公交和询问分别挂在对应端点上。

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 #include <vector>
  5 using namespace std;
  6 const int N=200010,K=19;
  7 struct ask {
  8     int y,id,s;
  9     ask () {}
 10     ask (int y,int id,int s):y(y),id(id),s(s) {}
 11 };
 12 int n,m,q,x,y,z,f[N][K],ans[N],c[N],sum[N];
 13 int cnt,dfn[N],fa[N],son[N],top[N],dep[N],size[N];
 14 int p,head[N],to[N],nxt[N];
 15 vector<int> h[N];
 16 vector<ask> A[N];
 17 inline int read() {
 18     int re=0; char ch=getchar();
 19     while (ch<‘0‘||ch>‘9‘) ch=getchar();
 20     while (ch>=‘0‘&&ch<=‘9‘) re=re*10+ch-48,ch=getchar();
 21     return re;
 22 }
 23 inline void add(int x,int y) {
 24     to[++p]=y; nxt[p]=head[x]; head[x]=p;
 25 }
 26 void tree_add(int x) {for (; x<=n; x+=(x&-x)) ++c[x];}
 27 int tree_sum(int x) {int re=0; for (; x; x-=(x&-x)) re+=c[x]; return re;}
 28 void dfs1(int x) {
 29     size[x]=1; dfn[x]=++cnt;
 30     for (int i=head[x]; i; i=nxt[i]) {
 31         dep[to[i]]=dep[x]+1;
 32         dfs1(to[i]);
 33         size[x]+=size[to[i]];
 34         if (size[to[i]]>size[son[x]]) son[x]=to[i];
 35     }
 36 }
 37 void dfs2(int x,int tp) {
 38     top[x]=tp;
 39     if (son[x]) dfs2(son[x],tp);
 40     for (int i=head[x]; i; i=nxt[i]) {
 41         if (to[i]==son[x]) continue;
 42         dfs2(to[i],to[i]);
 43     }
 44 }
 45 void dfs3(int x) {
 46     for (int i=head[x]; i; i=nxt[i]) {
 47         dfs3(to[i]);
 48         if (dep[f[to[i]][0]]<dep[f[x][0]]) f[x][0]=f[to[i]][0];
 49     }
 50 }
 51 void dfs4(int x) {
 52     for (int i=0; i<A[x].size(); i++) {
 53         A[x][i].s=tree_sum(dfn[A[x][i].y]+size[A[x][i].y]-1)-tree_sum(dfn[A[x][i].y]-1);
 54     }
 55     for (int i=0; i<h[x].size(); i++) tree_add(dfn[h[x][i]]);
 56     int tmp;
 57     for (int i=head[x]; i; i=nxt[i]) dfs4(to[i]);
 58     for (int i=0; i<A[x].size(); i++) {
 59         tmp=tree_sum(dfn[A[x][i].y]+size[A[x][i].y]-1)-tree_sum(dfn[A[x][i].y]-1);
 60         if (tmp==A[x][i].s) ans[A[x][i].id]++;
 61     }
 62 }
 63 int lca(int a,int b) {
 64     while (top[a]!=top[b]) {
 65         if (dep[top[a]]>dep[top[b]]) swap(a,b);
 66         b=fa[top[b]];
 67     }
 68     if (dep[a]>dep[b]) swap(a,b);
 69     return a;
 70 }
 71 int main() {
 72     n=read();
 73     for (int i=2; i<=n; i++) add(fa[i]=read(),i);
 74     dfs1(1); dfs2(1,1);
 75     m=read();
 76     for (int i=1; i<=n; i++) f[i][0]=i;
 77     for (int i=1; i<=m; i++) {
 78         x=read(); y=read();
 79         h[x].push_back(y); h[y].push_back(x);
 80         z=lca(x,y);
 81         if (dep[z]<dep[f[x][0]]) f[x][0]=z;
 82         if (dep[z]<dep[f[y][0]]) f[y][0]=z;
 83     }
 84     dfs3(1);
 85     for (int j=1; j<K; j++)
 86         for (int i=1; i<=n; i++)
 87             f[i][j]=f[f[i][j-1]][j-1];
 88     q=read();
 89     for (int i=1; i<=q; i++) {
 90         x=read(); y=read(); z=lca(x,y);
 91         if (x==y) continue;
 92         if (f[x][K-1]!=f[y][K-1]) {ans[i]=-1; continue;}
 93         for (int j=K-1; j>=0; j--) {
 94             if (dep[f[x][j]]>dep[z]) ans[i]+=(1<<j),x=f[x][j];
 95             if (dep[f[y][j]]>dep[z]) ans[i]+=(1<<j),y=f[y][j];
 96         }
 97         ans[i]++;
 98         if (z!=x && z!=y) A[x].push_back(ask(y,i,0));
 99     }
100     dfs4(1);
101     for (int i=1; i<=q; i++) printf("%d\n",ans[i]);
102     return 0;
103 }

原文地址:https://www.cnblogs.com/hnooo/p/10016132.html

时间: 2024-09-30 00:45:20

CF983E NN country [倍增][LCA][树状数组]的相关文章

HDU 6203 ping ping ping(dfs序+LCA+树状数组)

http://acm.hdu.edu.cn/showproblem.php?pid=6203 题意: n+1 个点 n 条边的树(点标号 0 ~ n),有若干个点无法通行,导致 p 组 U V 无法连通.问无法通行的点最少有多少个. 思路: 贪心思维,破坏两个点的LCA是最佳的.那么怎么判断现在在(u,v)之间的路径上有没有被破坏的点呢,如果没有的话那么此时就要破坏这个lca点.一开始我们要把询问按照u和v的lca深度从大到小排序,如果某个点需要被破坏,那么它的所有子节点都可以不再需要破坏别的点

POJ 2763 Housewife Wind(DFS序+LCA+树状数组)

Housewife Wind Time Limit: 4000MS   Memory Limit: 65536K Total Submissions: 11419   Accepted: 3140 Description After their royal wedding, Jiajia and Wind hid away in XX Village, to enjoy their ordinary happy life. People in XX Village lived in beauti

SUOI08 一收一行破 (lca+树状数组)

用一个差分树状数组维护一下每个深度的和,然后每次拿着路径端点和lca加一加减一减就行了 1 #include<bits/stdc++.h> 2 #define pa pair<int,int> 3 #define ll long long 4 using namespace std; 5 const int maxn=200020; 6 7 inline ll rd(){ 8 ll x=0;char c=getchar();int neg=1; 9 while(c<'0'||

LCA+树状数组 POJ 2763 Housewife Wind

题目传送门 题意:两种操作,问u到v的距离,并且u走到了v:把第i条边距离改成w 分析:根据DFS访问顺序,将树处理成链状的,那么回边处理成负权值,那么LCA加上BIT能够知道u到v的距离,BIT存储每条边的信息,这样第二种操作也能用BIT快速解决 利用RMQ的写法不知哪里写挫了,改用倍增法 /************************************************ * Author :Running_Time * Created Time :2015/10/6 星期二

HDU 3966 dfs序+LCA+树状数组

题目意思很明白: 给你一棵有n个节点的树,对树有下列操作: I c1 c2 k 意思是把从c1节点到c2节点路径上的点权值加上k D c1 c2 k 意思是把从c1节点到c2节点路径上的点权值减去k Q a 查询节点a的权值 数据大小 节点个数 n[1,50000], 操作次数 op[0,30000]; 不会树链剖分 故只有想其他的方法. 这道题有点类似今年上海网络赛的1003 ,不过那题我没做: 算法思路: 以节点1 为根,求出节点i 的 dfs序列 tim[i][2]; 其中tim[i][0

HDU 5296 Annoying problem LCA+树状数组

题解链接 Annoying problem Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 480    Accepted Submission(s): 146 Problem Description Coco has a tree, whose nodes are conveniently labeled by 1,2,-,n, w

BZOJ 2819 Nim 树链剖分/DFS序+LCA+树状数组

题意:给定一棵树,每个节点是一堆石子,给定两种操作: 1.改变x号节点的石子数量 2.用从x到y的路径上的所有堆石子玩一次Nim游戏,询问是否有必胜策略 Nim游戏有必胜策略的充要条件是所有堆的石子数异或起来不为零 这题首先一看就是树链剖分 然后题目很善良地告诉我们深搜会爆栈 于是我们可以选择广搜版的树链剖分 BFS序从左到右是深搜,从右到左是回溯,一遍BFS就够 单点修改区间查询还可以套用ZKW线段树 不过这题其实不用这么麻烦 有更简单的办法 详见 http://dzy493941464.is

【bzoj1146】[CTSC2008]网络管理Network 倍增LCA+dfs序+树状数组+主席树

题目描述 M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个部门之间协同工作,公司搭建了一个连接整个公司的通信网络.该网络的结构由N个路由器和N-1条高速光缆组成.每个部门都有一个专属的路由器,部门局域网内的所有机器都联向这个路由器,然后再通过这个通信子网与其他部门进行通信联络.该网络结构保证网络中的任意两个路由器之间都存在一条直接或间接路径以进行通信. 高速光缆的数据传输速度非常快,以至于利用光缆传输的延迟时间可以忽略.但是由于路由器老化,在这些

【bzoj2819】Nim DFS序+树状数组+倍增LCA

题目描述 著名游戏设计师vfleaking,最近迷上了Nim.普通的Nim游戏为:两个人进行游戏,N堆石子,每回合可以取其中某一堆的任意多个,可以取完,但不可以不取.谁不能取谁输.这个游戏是有必胜策略的.于是vfleaking决定写一个玩Nim游戏的平台来坑玩家.为了设计漂亮一点的初始局面,vfleaking用以下方式来找灵感:拿出很多石子,把它们聚成一堆一堆的,对每一堆编号1,2,3,4,...n,在堆与堆间连边,没有自环与重边,从任意堆到任意堆都只有唯一一条路径可到达.然后他不停地进行如下操