JZOJ5918【NOIP2018模拟10.20】Car
题目
最近比较懒,题目描述都直接截图了。
题目大意
给你一棵树,还有树上的几条路径,一条路径上的点到路径上其它任意点的代价为111。然后是一堆询问,问从一个点到另一个点的最小代价。
思路
一开始做这题时,就自然地往链剖或倍增方面想。
链剖想不出,于是就想倍增。
对于每个点,预处理出它花111的代价能到达的最远祖先。
然后倍增一下~
询问的时候,就将两个点的LCALCALCA求出,然后两个点往上跳,直到再跳就超过LCALCALCA为止。
所以问题就转化成了求两个点是否被一条路径直接连通,如果是,就加111,否则加222。
想不出来……
最后只能打暴力:将一个点作为根,然后粗暴地处理……
正解
事实证明我前面所思考的是正确的。
对于这个问题,其实可以转化成:是否有一条路径的两个端点分别在这两个点的子树内……
好像是很显然的,可我为什么想不到……
于是我们就可以求出它们的dfndfndfn序,这就成了一个平面上的问题。
每一条路径可以看做一个点,每一个询问可以看作一个矩形,求这个矩形当中是否有点出现。
然后就是一个简单的扫描线和树状数组了。
代码
有一点需要补充一下:
有的同学反映,这题递归会爆栈,所以要打人工栈。
反正我是没有爆栈……
后来听说xzb大爷想出一个不用dfs求dfn序的方法。
首先,我们可以通过拓扑排序之类的自底向上递推求出它们的siz等信息。(这题比较良心,由于每个点的父亲编号小于它,所以直接从后往前扫就好了。)
然后自顶向下(可以用bfs,这题直接从前往后扫就好了),
对于每个点分两种情况:
- 它是父亲的第一个儿子。那么它的dfndfndfn即为父亲加一。
- 否则,就是父亲上一个儿子的dfndfndfn加它的sizsizsiz。
然后就可以简单地求出dfndfndfn了。
正常的bfs中,上一个点就是父亲的上一个儿子。
不过这题不用bfs,直接记录就好了。
在此膜拜xzb
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 200000
#define M N
#define Q N
int n;
int last[N+1];
int siz[N+1],dfn[N+1];
int fa[N+1][19],dep[N+1];
inline void get_dfn(){//这是一种不需要dfs求dfn的方法,解释见上
dfn[1]=1;
for (int i=2;i<=n;++i){
if (last[fa[i][0]])
dfn[i]=dfn[last[fa[i][0]]]+siz[last[fa[i][0]]];
else
dfn[i]=dfn[fa[i][0]]+1;
last[fa[i][0]]=i;
}
}
inline int LCA(int u,int v){
if (dep[u]<dep[v])
swap(u,v);
for (int k=dep[u]-dep[v],i=0;k;k>>=1,++i)
if (k&1)
u=fa[u][i];
if (u==v)
return u;
for (int i=17;i>=0;--i)
if (fa[u][i]!=fa[v][i]){
u=fa[u][i];
v=fa[v][i];
}
return fa[u][0];
}
int m;
int up[N+1][19],dep2[N+1];
struct Oper{
int x,l,r;
int ty;
int num;
} o[M*2+Q*2+1];
int cnt;
bool cmp(const Oper &a,const Oper &b){
return a.x<b.x || a.x==b.x && a.ty<b.ty;
}
int t[N+1];
#define lowbit(x) ((x)&(-x))
inline void change(int x){
do{
t[x]++;
x+=lowbit(x);
}
while (x<=n);
}
inline int query(int x){
int res=0;
while (x){
res+=t[x];
x-=lowbit(x);
}
return res;
}
int ans[Q+1],cha[Q+1];
int main(){
freopen("car.in","r",stdin);
freopen("car.out","w",stdout);
scanf("%d",&n);
for (int i=2;i<=n;++i){
scanf("%d",&fa[i][0]);
e[++ne]={i,last[fa[i][0]]};
last[fa[i][0]]=e+ne;
}
for (int i=1;i<=n;++i)
dep[i]=dep[fa[i][0]]+1;//因为题目说每个点父亲的编号一定小于它,所以直接递推就好,下同。
for (int i=n;i>=1;--i)
siz[fa[i][0]]+=++siz[i];
get_dfn(1);
for (int i=1;i<=17;++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)
up[i][0]=i;//up[i][j]表示花费2^j的代价最远能到达的祖先
scanf("%d",&m);
for (int i=1;i<=m;++i){
int u,v;
scanf("%d%d",&u,&v);
if (dfn[u]>dfn[v])
swap(u,v);
//处理花费1代价能到达的最远祖先
int lca=LCA(u,v);
if (dep[lca]<dep[up[u][0]])
up[u][0]=lca;
if (dep[lca]<dep[up[v][0]])
up[v][0]=lca;
//加入操作列表中,相当于点(因为它有对称性,所以正着反着都加进去)
o[++cnt]={dfn[u],dfn[v],dfn[v],0,0};
o[++cnt]={dfn[v],dfn[u],dfn[u],0,0};
}
for (int i=n;i>=1;--i)
if (dep[up[i][0]]<dep[up[fa[i][0]][0]])
up[fa[i][0]][0]=up[i][0];//从底向上递推出每个点花费1代价到达的最远祖先
for (int i=1;i<=18;++i)
for (int j=1;j<=n;++j)
up[j][i]=up[up[j][i-1]][i-1];
int q;
scanf("%d",&q);
for (int i=1;i<=q;++i){
int u,v;
scanf("%d%d",&u,&v);
if (u==v)
continue;
if (up[u][18]!=up[v][18]){//判断两个点是否可以互相到达(因为N=200000,2^18>N,所以这是它能到达的最远祖先,因为这是一棵树,所以如果可达到的最远祖先不一样,那么他们就不能互相到达)
ans[i]=-1;
continue;
}
int lca=LCA(u,v);
if (lca==u || lca==v){//在同一条链上的情况
if (lca==u)
swap(u,v);
for (int j=17;j>=0;--j)
if (dep[up[u][j]]>dep[v])
u=up[u][j],ans[i]+=1<<j;//跳到LCA下的最远点
ans[i]++;
continue;
}
for (int j=17;j>=0;--j)
if (dep[up[u][j]]>dep[lca])
u=up[u][j],ans[i]+=1<<j;//跳到LCA下的最远点,下同
for (int j=17;j>=0;--j)
if (dep[up[v][j]]>dep[lca])
v=up[v][j],ans[i]+=1<<j;
//加入操作
o[++cnt]={dfn[u]-1,dfn[v],dfn[v]+siz[v]-1,1,i};
o[++cnt]={dfn[u]+siz[u],dfn[v],dfn[v]+siz[v]-1,-1,i};
}
sort(o+1,o+cnt+1,cmp);
for (int i=1;i<=cnt;++i)//扫描线求出二维数点问题
if (o[i].ty==0)
change(o[i].l);
else if (o[i].ty==1)
cha[o[i].num]=query(o[i].r)-query(o[i].l-1);
else
ans[o[i].num]+=((query(o[i].r)-query(o[i].l-1))-cha[o[i].num]?1:2);
for (int i=1;i<=q;++i)
printf("%d\n",ans[i]);
return 0;
}
总结
以后,看见有关树的题目,如果普通的倍增、链剖都做不出来,就得要往dfndfndfn序方面想……
JZOJ5918【NOIP2018模拟10.20】Car的更多相关文章
- [JZOJ NOIP2018模拟10.20 B组]
T1:原根(math) 题目链接: http://172.16.0.132/senior/#contest/show/2532/0 题目: 题解: 一个数m原根的个数是$\phi{(\phi{(m)} ...
- [JZOJ NOIP2018模拟10.20 A组]
由于T3数据出锅,还不清楚自己的分数...估分150,前100已经拿到了,T3的50没拍过(写的就是暴力怎么拍),感觉很不稳 考试的时候就是特别的困,大概是因为早上在房间里腐败...腐败完了才睡觉 T ...
- [jzoj NOIP2018模拟10.23]
丢分主要是下面几个方面: 1.T2代码交错了,有个特判没写丢了10分 2.T1线段树加等差数列写错了(其实二维差分就可以,但我当时不会) 3.T3思考再三还是为了10分写上了主席树,还是写错了 总体评 ...
- [jzoj NOIP2018模拟10.29]
OI生涯的最高分,来了纪中这么多天,在经历了这么多场“NOIP难度”的模拟赛之后,终于看到了真正的NOIP 今天考场上效率很高,很快码完了全部的题目,留下了足够的时间对拍和...发呆.不得不说看着电脑 ...
- [JZOJ NOIP2018模拟10.21]
考试之前我刚刚领略到了特判的重要性,没想到T2的两个子任务还是写挂了,丢了20分 考试的感觉不行,一路打的都是暴力,正解的思路想到一半就断了推不下去 T1:逛公园 题目链接: https://jzoj ...
- [JZOJ NOIP2018模拟10.19]
T1写炸了今天,期望70却落了个20...连链上的都没有写对 T3什么什么线段树分治套AC自动机,表示我完全自闭了,幸好考场上没有杠T3 总体比赛还是比较舒服,暴力分给的蛮足的,不像昨天那样 T1:林 ...
- [NOIP2018模拟10.15]比赛报告
闲扯 昨晚又颓到好晚,Yali的降智光环感觉持续至今... 题面好评 T1T3都玩过 逃) T1没看多久就开始写二分+并查集 然后T3看着眼熟想了一个多小时...结果啥都没想出来 赶紧看T2发现还是没 ...
- JZOJ5895【NOIP2018模拟10.5】旅游
题目 Description
- jzoj5929. 【NOIP2018模拟10.26】情书
动态规划: #include<bits/stdc++.h> using namespace std; int n,iv[30]; #define mo 998244353 typedef ...
随机推荐
- Nlog 日志框架简单教程
安装 Nuget获取 配置寻找 会自动寻找在应用程序目录下的NLog.config(大小写敏感) 如何配置config <?xml version="1.0" encodin ...
- 揭秘 Flink 1.9 新架构,Blink Planner 你会用了吗?
本文为 Apache Flink 新版本重大功能特性解读之 Flink SQL 系列文章的开篇,Flink SQL 系列文章由其核心贡献者们分享,涵盖基础知识.实践.调优.内部实现等各个方面,带你由浅 ...
- 概率dp——期望水题hdu4405
还是逆推,如果遇到跳板直接继承目标地的期望即可 #include<bits/stdc++.h> using namespace std; #define maxn 200005 doubl ...
- E: Sub-process /usr/bin/dpkg returned an error code (1)解决办法
解决方法: 先将info文件夹更名 sudo mv /var/lib/dpkg/info /var/lib/dpkg/info.bk 新建一个新的info文件夹 sudo mkdir /var/lib ...
- CNN 常用的几个模型
LeNet5 论文:http://yann.lecun.com/exdb/publis/pdf/lecun-01a.pdf LeNet-5:是Yann LeCun在1998年设计的用于手写数字识别的卷 ...
- (转)JS的splice()方法在for循环中的使用问题
在写JS代码时,我们常常使用 splice 函数来删除数组中的元素,因为 splice 函数会直接对数组进行修改,从而不需再自己写一个算法来移动数组中的其他元素填补到被删除的位置.splice 功能十 ...
- csp-s模拟测试89
csp-s模拟测试89 $T1$想了一会儿没什么思路,一看$T2$ $1e18$当场自闭打完暴力就弃了,$T3$看完题感觉要求$lca$和$dep$,手玩了一下样例发现$lca$很显然,$dep$貌 ...
- 为什么程序员都不喜欢使用switch,而是大量的 if……else if ?
作者:熊爸爸 原文:http://3g.163.com/tech/article/E02RDE6C0511SDDL.html 请用5秒钟的时间查看下面的代码是否存在bug. OK,熟练的程序猿应该已经 ...
- POJ 2932 平面扫描 /// 判断圆的包含关系
题目大意: 平面上有n个两两不相交的圆,给定圆的圆心(x,y)和半径 r 求所有最外层的 即 不包含于其他圆内部的圆 挑战258页 平面扫描 记录所有圆的左端和右端 排序后 逐一扫描 将到当前圆为止的 ...
- WebClient 上传文件 上传文件到服务器端
一直对于上传文件到服务器端困惑:以前,现在,学到了关于WebClient的post知识 瞬间对于上传文件到服务器觉得好轻松: 原理很简单:我们通过post服务器的页面:把本地的文件直接传递过去: 现在 ...