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 ...
随机推荐
- leetcode-220-存在重复元素③*
题目描述: 方法一:二叉搜索树+滑动窗口 方法二:桶排序 O(N) class Solution: def containsNearbyAlmostDuplicate(self, nums: List ...
- BZOJ 2660 (BJOI 2012) 最多的方案
Description 第二关和很出名的斐波那契数列有关,地球上的OIer都知道:F1=1, F2=2, Fi = Fi-1 + Fi-2,每一项都可以称为斐波那契数.现在给一个正整数N,它可以写成一 ...
- Mysql中“select ... for update”排他锁(转)
原帖地址 https://blog.csdn.net/claram/article/details/54023216 Mysql InnoDB 排他锁 用法: select … for update; ...
- WinDBG常用断点命令
WinDBG提供了多种设断点的命令: bp 命令是在某个地址 下断点, 可以 bp 0x7783FEB 也可以 bp MyApp!SomeFunction . 对于后者,WinDBG 会自动找到MyA ...
- 委托_deleget
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- Collection单列集合中的常用实现类
Collection 集合层次的根接口 List 有序 有索引 可以重复 ArrayList 底层数据结构是数组 查询快 增删快 线程不安全 效率高 LinkedList 底层数据结构是链表 查询慢 ...
- NEO4J 图数据库使用APOC数据导入
Neo4j 数据导入 一.安装与部署 直接在官网下载安装包安装,解压即可. 二.下载相应的jar包 apoc 包下载链接: https://github.com/neo4j-contrib/ne ...
- AngularJs 报错 Error: [$parse:lexerr]
参考:https://www.cnblogs.com/fangshidaima/p/6048071.html 错误: 根据错误找到报错行: $scope.$apply($scope.param1 = ...
- JavaScript变量名与函数名的命名规范
JavaScrip变量名与函数名的命名规范严格遵循以下5条: (1)首字符必须是字母.下划线.$,后跟任意的字母.数字.下划线.$ (2)严格区分大小写 (3)不能使用系统的关键字和保留字 (4)命名 ...
- 使用应用程序(Java/Python)访问MaxCompute Lightning进行数据开发
MaxCompute Lightning是MaxCompute产品的交互式查询服务,支持以PostgreSQL协议及语法连接访问Maxcompute项目,让您使用熟悉的工具以标准 SQL查询分析Max ...