题意

给定一棵树和若干条路线,每条路线相当于树上 x,y 之间的路径,途径路径上的每个点
给出若干个询问,每次询问从 u 到 v 至少需要利用几条路线
N,M,Q≤200000

题解

构建倍增数组g[i][j]表示从i点向上经过j条线路能到达的深度最小的点。
所以对于每一对询问的x,y,我们贪心地把它们提到深度大于等于lca地最大的点。记为x‘,y'然后判断是否有路径经过x’和y‘
然后各种情况特判(无解,x‘,y’为lca,有无路径经过x‘,y’)
然后对于判断是否路径经过x‘,y’。
可以用树状数组维护 DFS 序
DFS 遍历整棵树,递归进入 x 时,扫描路线 (x,y),把 y 在 DFS 序上对应位置 +1,
x,y 可直达,当且仅当 x 子树中的节点使 DFS 序上 y 对应区间的和发生了变化
(调了半天代码最后没调出来,最后屈辱地看了题解)
 #include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
const int N=;
const int D=;
struct node{
int a,b;
};
vector<int> road[N],h[N];
vector<node> q[N];
int n,m,t,fa[N][D+],w[N][D+],head[N],sum[N],ans[N],g[N];
int cnt,tot,dep[N],id[N],size[N],c[N];
int lowbit(int x){
return x&-x;
}
void update(int x){
for(int i=x;i<=n;i+=lowbit(i)){
c[i]+=;
}
}
int check(int x){
int tmp=;
for(int i=x;i>=;i-=lowbit(i)){
tmp+=c[i];
}
return tmp;
}
struct edge{
int to,nxt;
}e[N*];
void add(int u,int v){
cnt++;
e[cnt].nxt=head[u];
e[cnt].to=v;
head[u]=cnt;
}
void dfs1(int u,int deep){
dep[u]=deep;
id[u]=++tot;
size[u]=;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==fa[u][])continue;
dfs1(v,deep+);
size[u]+=size[v];
}
}
void dfs2(int u){
g[u]=u;
// cout<<u<<"ksdjfksdf"<<endl;
for(int i=;i<h[u].size();i++){
// cout<<h[u][i]<<endl;
if(dep[h[u][i]]<dep[g[u]])g[u]=h[u][i];
}
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
// cout<<v<<" "<<fa[u][0]<<endl;
if(v==fa[u][])continue;
dfs2(v);
if(dep[g[v]]<dep[g[u]])g[u]=g[v];
}
w[u][]=g[u];
}
int lca(int x,int y){
if(dep[x]<dep[y])swap(x,y);
for(int i=D;i>=;i--){
if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
}
if(x==y)return x;
for(int i=D;i>=;i--){
if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
}
return fa[x][];
}
node work(int x,int LCA){
if(x==LCA)return (node){x,};
int tmp=;
for(int i=D;i>=;i--)
if(dep[w[x][i]]>dep[LCA])x=w[x][i],tmp+=(<<i);
if(w[x][]==x)return (node){x,-};
return (node){x,tmp};
}
int main(){
scanf("%d",&n);
for(int i=;i<=n;i++){
scanf("%d",&fa[i][]);
add(fa[i][],i);
add(i,fa[i][]);
}
dfs1(,);
for(int i=;i<=D;i++)
for(int j=;j<=n;j++){
fa[j][i]=fa[fa[j][i-]][i-];
}
scanf("%d",&m);
for(int i=;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
int c=lca(x,y);
// cout<<c<<endl;
if(id[x]>id[y])swap(x,y);
road[id[x]].push_back(id[y]);
if(dep[c]<dep[x])h[x].push_back(c);
if(dep[c]<dep[y])h[y].push_back(c);
}
dfs2();
for(int i=;i<=n;i++){
// cout<<g[i]<<endl;
}
for(int i=;i<=D;i++)
for(int j=;j<=n;j++){
w[j][i]=w[w[j][i-]][i-];
// cout<<w[j][i]<<endl;
}
scanf("%d",&t);
for(int i=;i<=t;i++){
int x,y;
scanf("%d%d",&x,&y);
int c=lca(x,y);
node A=work(x,c);
node B=work(y,c);
if(A.b==-||B.b==-){
ans[i]=-;
continue;
}
if(x==c||y==c){
ans[i]=A.b+B.b+;
continue;
}
ans[i]=A.b+B.b+;
x=A.a;
y=B.a;
if(id[x]>id[y])swap(x,y);
q[id[x]-].push_back((node){y,-i});
q[id[x]+size[x]-].push_back((node){y,i});
}
for(int i=;i<=n;i++){
for(int j=;j<road[i].size();j++)update(road[i][j]);
for(int j=;j<q[i].size();j++){
int num=(q[i][j].b>?:-);
int x=q[i][j].a;int y=q[i][j].b*num;
sum[y]+=num*(check(id[x]+size[x]-)-check(id[x]-));
}
}
for(int i=;i<=t;i++){
printf("%d\n",ans[i]-(sum[i]>));
}
return ;
}

CF983E NN country(倍增,差分)的更多相关文章

  1. cf983E NN Country (倍增+dfs序+树状数组)

    首先可以求出从某点做$2^k$次车能到的最浅的点,这个只要dfs一下,把它的孩子能到的最浅的点更新过来就可以 然后倍增地往上跳,不能跳到lca的上面,记录坐车的次数ans 此时有三种情况(设最远能跳到 ...

  2. [CF983E]NN country

    题意:给一棵树,有许多条巴士线路$(a_i,b_i)$(巴士在路径上每个点都会停车),多次询问从一点到另一点最少要坐多少次巴士 首先dfs一遍预处理出一个点向上坐$2^k$次巴士能到的最浅点,于是我们 ...

  3. 【CodeForces】983 E. NN country 树上倍增+二维数点

    [题目]E. NN country [题意]给定n个点的树和m条链,q次询问一条链(a,b)最少被多少条给定的链覆盖.\(n,m,q \leq 2*10^5\). [算法]树上倍增+二维数点(树状数组 ...

  4. 【codeforces 983E】NN country

    Description In the NN country, there are n cities, numbered from 1 to n, and n−1 roads, connecting t ...

  5. Codeforces 983E - NN country(贪心+倍增优化)

    Codeforces 题面传送门 & 洛谷题面传送门 一道(绝对)偏简单的 D1E,但是我怕自己过若干年(大雾)忘了自己的解法了,所以过来水篇题解( 首先考虑怎么暴力地解决这个问题,不难发现我 ...

  6. NOIP2016提高组Day1T2 天天爱跑步 树链剖分 LCA 倍增 差分

    原文链接https://www.cnblogs.com/zhouzhendong/p/9275606.html 题目传送门 - 洛谷P1600 题目传送门 - LOJ#2359 题目传送门 - Vij ...

  7. NOIP2015 运输计划 - 二分 + 树链剖分 / (倍增 + 差分)

    BZOJ CodeVS Uoj 题目大意: 给一个n个点的边带权树,给定m条链,你可以选择树中的任意一条边,将它置为0,使得最长的链长最短. 题目分析: 最小化最大值,二分. 二分最短长度mid,将图 ...

  8. Codeforces983E. NN country

    新鲜出炉! $n \leq 200000$的树,给$m \leq 200000$条链,$q \leq 200000$个询问,每次问一条询问链最少用m条中的几条给定链覆盖其所有边,可能无解. 首先确定一 ...

  9. 多校联训 DS 专题

    CF1039D You Are Given a Tree 容易发现,当 \(k\) 不断增大时,答案不断减小,且 \(k\) 的答案不超过 \(\lfloor\frac {n}{k}\rfloor\) ...

随机推荐

  1. URAL 1297 后缀数组+线段树

    思路: 论文题--*n 倒过来接上 分奇偶讨论 求LCP 搞棵线段树即可 //By SiriusRen #include <cstdio> #include <cstring> ...

  2. struts2学习之基础笔记2

    6.5 Struts2 的基本配置 1web.xml 作用:加载核心过滤器 格式: <filter> ``````` </filter> <filter-mapping& ...

  3. (转载)Android:学习AIDL,这一篇文章就够了(下)

    前言 上一篇博文介绍了关于AIDL是什么,为什么我们需要AIDL,AIDL的语法以及如何使用AIDL等方面的知识,这一篇博文将顺着上一篇的思路往下走,接着介绍关于AIDL的一些更加深入的知识.强烈建议 ...

  4. 修改input标签中的placeholder样式

    input::-webkit-input-placeholder { color: #fff !important; } input:-moz-placeholder { color: #fff !i ...

  5. js递归获取html页面所有标签

    js原生递归获取,直接源码 : <script> var child = document.children; var arr = [];//用来存放获取到的所有的标签 function ...

  6. IIFE 萌新学习笔记

    立即执行函数表达式(IIFE) IIFE:Immediately-Invoked Function Expression(立即执行函数表达式) 一 常用写法: //经常使用的写法(function() ...

  7. BZOJ 1085 / LOJ 2151 [SCOI2005]骑士精神 (剪枝/A*迭代搜索)

    题目大意:略 直接爆搜会T,我们优化一下,统计出当前棋盘和目标棋盘不同的位置的数量k,那么当前棋盘变成目标棋盘最少的移动次数是k-1 每次选择一个最大深度ma,那么如果当前走了dep步,显然必须保证d ...

  8. shell的通俗理解

    (引自:https://zhidao.baidu.com/question/557066905.html) [一] shell的含义: 首先shell的英文含义是“壳”: 它是相对于内核来说的,因为它 ...

  9. H5学习_番外篇_PHP数据库操作

    1. 文件操作 1.1 打开关闭文件 fopen() resource fopen ( string filename, string mode [, bool use_include_path [, ...

  10. poj_2187凸包,暴力解法

    #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #i ...