题目



最近比较懒,题目描述都直接截图了。

题目大意

给你一棵树,还有树上的几条路径,一条路径上的点到路径上其它任意点的代价为111。然后是一堆询问,问从一个点到另一个点的最小代价。


思路

一开始做这题时,就自然地往链剖或倍增方面想。

链剖想不出,于是就想倍增。

对于每个点,预处理出它花111的代价能到达的最远祖先。

然后倍增一下~

询问的时候,就将两个点的LCALCALCA求出,然后两个点往上跳,直到再跳就超过LCALCALCA为止。

所以问题就转化成了求两个点是否被一条路径直接连通,如果是,就加111,否则加222。

想不出来……

最后只能打暴力:将一个点作为根,然后粗暴地处理……


正解

事实证明我前面所思考的是正确的。

对于这个问题,其实可以转化成:是否有一条路径的两个端点分别在这两个点的子树内……

好像是很显然的,可我为什么想不到……

于是我们就可以求出它们的dfndfndfn序,这就成了一个平面上的问题。

每一条路径可以看做一个点,每一个询问可以看作一个矩形,求这个矩形当中是否有点出现。

然后就是一个简单的扫描线和树状数组了。


代码

有一点需要补充一下:

有的同学反映,这题递归会爆栈,所以要打人工栈。

反正我是没有爆栈……

后来听说xzb大爷想出一个不用dfs求dfn序的方法。

首先,我们可以通过拓扑排序之类的自底向上递推求出它们的siz等信息。(这题比较良心,由于每个点的父亲编号小于它,所以直接从后往前扫就好了。)

然后自顶向下(可以用bfs,这题直接从前往后扫就好了),

对于每个点分两种情况:

  1. 它是父亲的第一个儿子。那么它的dfndfndfn即为父亲加一。
  2. 否则,就是父亲上一个儿子的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的更多相关文章

  1. [JZOJ NOIP2018模拟10.20 B组]

    T1:原根(math) 题目链接: http://172.16.0.132/senior/#contest/show/2532/0 题目: 题解: 一个数m原根的个数是$\phi{(\phi{(m)} ...

  2. [JZOJ NOIP2018模拟10.20 A组]

    由于T3数据出锅,还不清楚自己的分数...估分150,前100已经拿到了,T3的50没拍过(写的就是暴力怎么拍),感觉很不稳 考试的时候就是特别的困,大概是因为早上在房间里腐败...腐败完了才睡觉 T ...

  3. [jzoj NOIP2018模拟10.23]

    丢分主要是下面几个方面: 1.T2代码交错了,有个特判没写丢了10分 2.T1线段树加等差数列写错了(其实二维差分就可以,但我当时不会) 3.T3思考再三还是为了10分写上了主席树,还是写错了 总体评 ...

  4. [jzoj NOIP2018模拟10.29]

    OI生涯的最高分,来了纪中这么多天,在经历了这么多场“NOIP难度”的模拟赛之后,终于看到了真正的NOIP 今天考场上效率很高,很快码完了全部的题目,留下了足够的时间对拍和...发呆.不得不说看着电脑 ...

  5. [JZOJ NOIP2018模拟10.21]

    考试之前我刚刚领略到了特判的重要性,没想到T2的两个子任务还是写挂了,丢了20分 考试的感觉不行,一路打的都是暴力,正解的思路想到一半就断了推不下去 T1:逛公园 题目链接: https://jzoj ...

  6. [JZOJ NOIP2018模拟10.19]

    T1写炸了今天,期望70却落了个20...连链上的都没有写对 T3什么什么线段树分治套AC自动机,表示我完全自闭了,幸好考场上没有杠T3 总体比赛还是比较舒服,暴力分给的蛮足的,不像昨天那样 T1:林 ...

  7. [NOIP2018模拟10.15]比赛报告

    闲扯 昨晚又颓到好晚,Yali的降智光环感觉持续至今... 题面好评 T1T3都玩过 逃) T1没看多久就开始写二分+并查集 然后T3看着眼熟想了一个多小时...结果啥都没想出来 赶紧看T2发现还是没 ...

  8. JZOJ5895【NOIP2018模拟10.5】旅游

    题目 Description

  9. jzoj5929. 【NOIP2018模拟10.26】情书

    动态规划: #include<bits/stdc++.h> using namespace std; int n,iv[30]; #define mo 998244353 typedef ...

随机推荐

  1. CSS——用户界面样式

    所谓的界面样式, 就是更改一些用户操作样式, 比如 更改用户的鼠标样式, 表单轮廓等.但是比如滚动条的样式改动受到了很多浏览器的抵制,因此我们就放弃了. 防止表单域拖拽 鼠标样式cursor 设置或检 ...

  2. LUOGU P2294 [HNOI2005]狡猾的商人(差分约束)

    [传送门] (https://www.luogu.org/problemnew/show/P2294) 解题思路 差分约束.先总结一下差分约束,差分约束就是解决一堆不等式混在一起,左边是差的形式,右边 ...

  3. 服务器断过一次电之后,mysql启动不了了

    公司内部服务器,周末会直接拉闸断电,之前也没问题,但这次回来发现mysql启动不了了. service mysqld start 提示: Starting MySQL.The server quit ...

  4. 尚学linux课程---4、linux网络配置及linux文件

    尚学linux课程---4.linux网络配置及linux文件 一.总结 一句话总结: linux下的etc目录是配置文件的目录,所以很多的文件配置操作都可以看到它的身影:比如 init系列命名,比如 ...

  5. 763 Hex Conversion

    原题网址:http://www.lintcode.com/zh-cn/problem/hex-conversion/ Given a decimal number n and an integer k ...

  6. https://www.cnblogs.com/chinabin1993/p/9848720.html

    转载:https://www.cnblogs.com/chinabin1993/p/9848720.html 这段时间一直在用vue写项目,vuex在项目中也会依葫芦画瓢使用,但是总有一种朦朦胧胧的感 ...

  7. AutoMapper Profile用法

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using AutoMappe ...

  8. day3:python运算符及数据类型(str)(int)

    运算符 算数运算 :a = 10 * 10赋值运算:a = a + 1 a+=1 比较运算:a = 1 > 5 逻辑运算: a = 1>6 or 1==1   a = 1 and b = ...

  9. PagedListCore的使用

    关于在Core2.0中PagedListCore实现分页 一.引言 开发中在对大量数据展示的时候,出于对性能的考虑,我们往往是使用分页功能(用户需要那一页我们就返回给他那一页,而不是一次性加载所有的数 ...

  10. 封装一个C#日志类Loger

    public class Loger { /// <summary> /// 写入日志 /// </summary> /// <param name="cont ...