Codeforces 题面传送门 & 洛谷题面传送门

一道(绝对)偏简单的 D1E,但是我怕自己过若干年(大雾)忘了自己的解法了,所以过来水篇题解(

首先考虑怎么暴力地解决这个问题,不难发现我们每一步肯定会贪心,贪心地跳到所有经过当前点的公交线路中另一端最浅的位置,直到到达两点的 \(\text{LCA}\) 为止。不难发现上述过程可以倍增优化,具体来说我们记 \(nxt_{i,j}\) 表示从 \(i\) 开始走 \(2^j\) 步最浅能够到达哪里,那么我们可以一面树剖求出经过每个点能够到达深度最浅的节点,一面倍增往上跳知道跳到深度 \(<\text{LCA}(u,v)\) 的位置为止。

不过上述算法有一个漏洞,就是在我们到达 \(\text{LCA}(u,v)\) 的前一步到达的点 \(u',v'\) 很可能已经可以通过某条公交线相连了,此时我们大可不必再花费 \(2\) 的代价跳到 \(\text{LCA}(u,v)\),直接一步就可以搞定,答案需减一。因此考虑再倍增求出 \(u,v\) 到达 \(\text{LCA}(u,v)\) 之前上一步到达的节点 \(u',v'\),不难发现,由于 \(u',v'\) 之间不存在祖先关系,因此 \(u',v'\) 存在公交线相连的充要条件是存在某个公交线,两个端点分别在 \(u',v'\) 子树内,这个可以 DFS 序+离线二维数点/在线主席树求出。

时间复杂度 \(n\log^2n\),但显然有复杂度更优秀/更好写的 implementation,比方说树剖换成 set 启发式合并代码可以少三十多行,换成线段树合并可以少一个 \(\log\)。

我竟然写这么短的题解,我怕不是精神不正常(大雾

代码(这种题我都能码 171 行……zz 这题我实现得跟 sh*t 一样):

const int MAXN=2e5;
const int LOG_N=18;
const int INF=0x3f3f3f3f;
int n,m,qu,qn,hd[MAXN+5],to[MAXN+5],nxt[MAXN+5],ec=0;
void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
int siz[MAXN+5],wson[MAXN+5],dep[MAXN+5],fa[MAXN+5][LOG_N+2];
int top[MAXN+5],dfn[MAXN+5],tim=0,edt[MAXN+5];
void dfs1(int x){
siz[x]=1;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];dep[y]=dep[x]+1;
dfs1(y);siz[x]+=siz[y];
if(siz[y]>siz[wson[x]]) wson[x]=y;
}
}
void dfs2(int x,int tp){
top[x]=tp;dfn[x]=++tim;if(wson[x]) dfs2(wson[x],tp);
for(int e=hd[x];e;e=nxt[e]) if(to[e]!=fa[x][0]&&to[e]!=wson[x])
dfs2(to[e],to[e]);
edt[x]=tim;
}
int getlca(int x,int y){
while(top[x]^top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
x=fa[top[x]][0];
} if(dep[x]<dep[y]) swap(x,y);
return y;
}
struct node{int l,r,val,lz;} s[MAXN*4+5];
void pushup(int k){s[k].val=min(s[k<<1].val,s[k<<1|1].val);}
void build(int k,int l,int r){
s[k].l=l;s[k].r=r;s[k].lz=INF;if(l==r) return s[k].val=INF,void();
int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);pushup(k);
}
void pushdown(int k){
if(s[k].lz!=INF){
chkmin(s[k<<1].val,s[k].lz);chkmin(s[k<<1|1].val,s[k].lz);
chkmin(s[k<<1].lz,s[k].lz);chkmin(s[k<<1|1].lz,s[k].lz);
s[k].lz=INF;
}
}
void modify(int k,int l,int r,int x){
if(l<=s[k].l&&s[k].r<=r){
chkmin(s[k].val,x);chkmin(s[k].lz,x);
return;
} pushdown(k);int mid=s[k].l+s[k].r>>1;
if(r<=mid) modify(k<<1,l,r,x);
else if(l>mid) modify(k<<1|1,l,r,x);
else modify(k<<1,l,mid,x),modify(k<<1|1,mid+1,r,x);
pushup(k);
}
int query(int k,int x){
if(s[k].l==s[k].r) return s[k].val;pushdown(k);
pushdown(k);int mid=s[k].l+s[k].r>>1;
if(x<=mid) return query(k<<1,x);
else return query(k<<1|1,x);
}
void jumpath(int x,int y,int v){
while(top[x]^top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
modify(1,dfn[top[x]],dfn[x],v);
x=fa[top[x]][0];
} if(dep[x]<dep[y]) swap(x,y);
modify(1,dfn[y],dfn[x],v);
}
int get_kanc(int x,int k){
for(int i=LOG_N;~i;i--) if(k>>i&1) x=fa[x][i];
return x;
}
int nt[MAXN+5][LOG_N+2],cnt[MAXN+5],ans[MAXN+5],mark[MAXN+5];
int step(int x,int d){
if(dep[x]<=d) return 0;
if(dep[nt[x][LOG_N]]>d) return -1;int cnt=0;
for(int i=LOG_N;~i;i--) if(dep[nt[x][i]]>d) x=nt[x][i],cnt|=(1<<i);
return cnt+(dep[x]>d);
}
int get_kstp(int x,int k){
for(int i=LOG_N;~i;i--) if(k>>i&1) x=nt[x][i];
return x;
}
vector<int> pts[MAXN+5];
struct qry{
int x,y,t,id;
bool operator <(const qry &rhs) const{
return x<rhs.x;
}
} q[MAXN*4+5];
void add_rec(int x1,int x2,int y1,int y2,int id){
// printf("%d %d %d %d %d\n",x1,x2,y1,y2,id);
q[++qn]={x2,y2,1,id};q[++qn]={x1-1,y2,-1,id};
q[++qn]={x2,y1-1,-1,id};q[++qn]={x1-1,y1-1,1,id};
}
struct fenwick{
int t[MAXN+5];
void add(int x,int v){for(int i=x;i<=n;i+=(i&(-i))) t[i]+=v;}
int query(int x){int ret=0;for(int i=x;i;i&=(i-1)) ret+=t[i];return ret;}
} tr;
int main(){
scanf("%d",&n);
for(int i=2;i<=n;i++) scanf("%d",&fa[i][0]),adde(fa[i][0],i);
dfs1(1);dfs2(1,1);build(1,1,n);
for(int i=1;i<=n;i++) modify(1,dfn[i],dfn[i],dep[i]);
scanf("%d",&m);
for(int i=1,x,y;i<=m;i++){
scanf("%d%d",&x,&y);
pts[dfn[x]].pb(dfn[y]);pts[dfn[y]].pb(dfn[x]);
// printf("(%d %d)\n",dfn[x],dfn[y]);
jumpath(x,y,dep[getlca(x,y)]);
}
for(int i=1;i<=LOG_N;i++) for(int j=1;j<=n;j++) fa[j][i]=fa[fa[j][i-1]][i-1];
for(int i=1;i<=n;i++) nt[i][0]=get_kanc(i,dep[i]-query(1,dfn[i]));
for(int i=1;i<=LOG_N;i++) for(int j=1;j<=n;j++) nt[j][i]=nt[nt[j][i-1]][i-1];
scanf("%d",&qu);
for(int i=1;i<=qu;i++){
int x,y,l;scanf("%d%d",&x,&y);l=getlca(x,y);
int sx=step(x,dep[l]),sy=step(y,dep[l]);
if(!~sx||!~sy) ans[i]=-1;
else{
ans[i]=sx+sy;
if(sx!=0&&sy!=0){
int ax=get_kstp(x,sx-1);
int ay=get_kstp(y,sy-1);
add_rec(dfn[ax],edt[ax],dfn[ay],edt[ay],i);
}
}
}
// for(int i=1;i<=qu;i++) printf("%d\n",ans[i]);
sort(q+1,q+qn+1);int cur=1;
for(int i=1;i<=qn;i++){
while(cur<=q[i].x){
for(int y:pts[cur]) tr.add(y,1);
cur++;
} cnt[q[i].id]+=q[i].t*tr.query(q[i].y);
// printf("%d %d %d\n",q[i].x,q[i].y,tr.query(q[i].y));
}
for(int i=1;i<=qu;i++) printf("%d\n",ans[i]-(cnt[i]>0));
return 0;
}

Codeforces 983E - NN country(贪心+倍增优化)的更多相关文章

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

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

  2. CF983E NN country(倍增,差分)

    题意 给定一棵树和若干条路线,每条路线相当于树上 x,y 之间的路径,途径路径上的每个点 给出若干个询问,每次询问从 u 到 v 至少需要利用几条路线 N,M,Q≤200000 题解 构建倍增数组g[ ...

  3. CodeForces - 1175E Minimal Segment Cover (倍增优化dp)

    题意:给你n条线段[l,r]以及m组询问,每组询问给出一组[l,r],问至少需要取多少个线段可以覆盖[l,r]区间中所有的点. 如果贪心地做的话,可以求出“从每个左端点l出发选一条线段可以到达的最右端 ...

  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 356D 倍增优化背包

    题目链接:http://codeforces.com/contest/356/problem/D 思路(官方题解):http://codeforces.com/blog/entry/9210 此题需要 ...

  6. codeforces 704B - Ant Man 贪心

    codeforces 704B - Ant Man 贪心 题意:n个点,每个点有5个值,每次从一个点跳到另一个点,向左跳:abs(b.x-a.x)+a.ll+b.rr 向右跳:abs(b.x-a.x) ...

  7. POJ 1014 Dividing(多重背包, 倍增优化)

    Q: 倍增优化后, 还是有重复的元素, 怎么办 A: 假定重复的元素比较少, 不用考虑 Description Marsha and Bill own a collection of marbles. ...

  8. CodeForces - 50A Domino piling (贪心+递归)

    CodeForces - 50A Domino piling (贪心+递归) 题意分析 奇数*偶数=偶数,如果两个都为奇数,最小的奇数-1递归求解,知道两个数都为1,返回0. 代码 #include ...

  9. HZOJ 20190727 随(倍增优化dp)

    达哥T1 实际上还是挺难的,考试时只qj20pts,还qj失败 因为他专门给出了mod的范围,所以我们考虑把mod加入时间复杂度. $50\%$算法: 考虑最暴力的dp,设$f[i][j]$表示进行$ ...

随机推荐

  1. 初学python-day9 函数1(已更新)

    函数 一.函数基础 1.什么是函数 在一个完整的项目中,某些功能会被重复使用,那么会将代码段封装成函数,当我们要使用的时候,直接调用即可. 函数是可以实现一定的小程序或者功能. 优点: 增加了代码的重 ...

  2. 脚本注入1(boolean&&get)

    现在,我们回到之前,练习脚本支持的布尔盲注(get型). 布尔盲注的应用场景是查询成功和失败时回显不同,且存在注入点的地方. 这里以Less-8为例: 发现查询成功时,会显示:失败则无回显. 同时发现 ...

  3. 【UE4 C++】学习笔记汇总

    UE4 概念知识 基础概念--文件结构.类型.反射.编译.接口.垃圾回收.序列化[导图] GamePlay架构[导图] 类的继承层级关系[导图] 反射机制 垃圾回收机制/算法 序列化 Actor 的生 ...

  4. Python:Ubuntu上出现错误 Could not load dynamic library 'libnvinfer.so.6' / 'libnvinfer_plugin.so.6'

    运行一个py文件,出现如下的错误,原因是没有找到 libnvinfer.so.6 相关库的文件. 1 2021-01-04 18:41:17.324477: W tensorflow/stream_e ...

  5. P2472 [SCOI2007]蜥蜴(最大流)

    P2472 [SCOI2007]蜥蜴 自己第一道独立做题且一遍AC的网络流题纪念... 看到这道题我就想到网络流建图的方式了... 首先根据每个高度,我们将每个点拆成两个点限流.之后根据跳的最大距离, ...

  6. LCA-离线tarjan模板

    /* *算法引入: *树上两点的最近公共祖先; *对于有根树的两个结点u,v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u,v的祖先且x的深度尽可能大; *对于x来说,从u到v的路径一定 ...

  7. 绑定socket描述符到一个网络设备

           网络编程中有时明明用eth0的地址来bind一个udp套接口, 可是发出去的包却是从eht1走的, 在网上找到这么一段话解释该问题:           在多 IP/网卡主机上,UDP ...

  8. IM服务器:开发一个高并发的IM服务器难在哪

    IM服务器要实现的最基本功能就是消息的转发.--好像是一句废话! 这就意味着IM服务器要为每个登录用户创建一个与该用户信息相关的内存上下文,为方便描述我们在这里称之为:user_context.use ...

  9. H3C 三层交换基于IP限速

    一.背景 目前百度爬虫爬取业务总是按照自己的性能进行抓取客户数据,从来不考虑客户端的网络承受能力,导致客户端网络带宽超出预算范围,因此在客户端方面针对百度的无限制抓取采取相应的策略. 二.解决方案: ...

  10. 《手把手教你》系列技巧篇(三十七)-java+ selenium自动化测试-日历时间控件-上篇(详解教程)

    1.简介 我们在实际工作中,有可能遇到有些web产品,网页上有一些时间选择,然后支持按照不同时间段范围去筛选数据.网页上日历控件一般,是一个文本输入框,鼠标点击,就会弹出日历界面,可以选择具体日期.这 ...