题目



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

题目大意

给你一棵树,还有树上的几条路径,一条路径上的点到路径上其它任意点的代价为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. leetcode-220-存在重复元素③*

    题目描述: 方法一:二叉搜索树+滑动窗口 方法二:桶排序 O(N) class Solution: def containsNearbyAlmostDuplicate(self, nums: List ...

  2. BZOJ 2660 (BJOI 2012) 最多的方案

    Description 第二关和很出名的斐波那契数列有关,地球上的OIer都知道:F1=1, F2=2, Fi = Fi-1 + Fi-2,每一项都可以称为斐波那契数.现在给一个正整数N,它可以写成一 ...

  3. Mysql中“select ... for update”排他锁(转)

    原帖地址 https://blog.csdn.net/claram/article/details/54023216 Mysql InnoDB 排他锁 用法: select … for update; ...

  4. WinDBG常用断点命令

    WinDBG提供了多种设断点的命令: bp 命令是在某个地址 下断点, 可以 bp 0x7783FEB 也可以 bp MyApp!SomeFunction . 对于后者,WinDBG 会自动找到MyA ...

  5. 委托_deleget

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  6. Collection单列集合中的常用实现类

    Collection 集合层次的根接口 List 有序 有索引 可以重复 ArrayList 底层数据结构是数组 查询快 增删快 线程不安全 效率高 LinkedList 底层数据结构是链表 查询慢 ...

  7. NEO4J 图数据库使用APOC数据导入

       Neo4j 数据导入 一.安装与部署 直接在官网下载安装包安装,解压即可. 二.下载相应的jar包 apoc 包下载链接: https://github.com/neo4j-contrib/ne ...

  8. AngularJs 报错 Error: [$parse:lexerr]

    参考:https://www.cnblogs.com/fangshidaima/p/6048071.html 错误: 根据错误找到报错行: $scope.$apply($scope.param1 = ...

  9. JavaScript变量名与函数名的命名规范

    JavaScrip变量名与函数名的命名规范严格遵循以下5条: (1)首字符必须是字母.下划线.$,后跟任意的字母.数字.下划线.$ (2)严格区分大小写 (3)不能使用系统的关键字和保留字 (4)命名 ...

  10. 使用应用程序(Java/Python)访问MaxCompute Lightning进行数据开发

    MaxCompute Lightning是MaxCompute产品的交互式查询服务,支持以PostgreSQL协议及语法连接访问Maxcompute项目,让您使用熟悉的工具以标准 SQL查询分析Max ...