P4899 [IOI2018] werewolf 狼人 题解

题目描述

省流:

\(n\) 个点,\(m\) 条边,\(q\) 次询问,对于每一次询问,给定一个起点 \(S\) 和终点 \(T\) ,能否找到一条路径,前半程不能走 \(0\thicksim L-1\) 这些点,后半程不能走 \(R+1\thicksim N-1\) 这些点。中途必须有一个点在\(L\thicksim R\)之间。

题目分析

首先对于这种限定了走的边的属性,或者走的点的属性的路径题,自然想到Kruskal重构树,然后注意到城市从 \(0\) 开始标号很可恶,所以我们就可以将所有标号加一,并且转化题意,对于前半段,我们只走 \(L\thicksim N\) 这些点,对于后半程,我们只走 $1\thicksim R $ 这些点,那么对于这样的要求,我们就可以建立Kruskal重构树了。首先分析边的权值,我们知道,走了一条边,就意味着会经过这条边连接的两个端点,所以说对于前半段来说,我们可以以\(min(x,y)\)为边权,从大到小排序建立一棵Kruskal重构树。因为我们对一条边所定的限制,就是尽量走大的编号,而界定前半程能否走这一条边的限制,即是会不会走到因为太小而不合法的编号,而对于后半程来说,同理可得,以\(max(x,y)\)为边权,从小到大排序建立一棵Kruskal重构树。

那么建立好重构树之后,我们就可以将路程分为三段,前半程,转折点,后半程,那么前半程和后半程都必须可达这个转折点,所以合法的转折点也就是前半程可达的点和后半程可达的点的交集。那么我们可以在前半程的Kruskal重构树上跳到权值大于等于\(L\)的最浅的结点,那么前半程可达的点一定在该点的子节点之中,对于后半程同理,于是我们就将问题转化成了求这样两个节点囊括的子节点有没有交集。

对于这样的问题,我们可以使用主席树解决。我们都知道一个点的编号与其\(dfn\)序是一一对应的,所以我们暂时可以用\(dfn\)序代替点的标号,我们在两棵树上先跑一个\(dfn\)序,然后按照前半程树的\(dfn\)序来将后半程的\(dfn\)加入主席树。当我们对于一个查询的时候,我们只需要先将前半程对应的祖先的所囊括的子节点的\(dfn\)序的左右端点作为查询的左右两数,让后查询是否存在值在所询问的后半程对应的祖先的\(dfn\)序的左右端点之间,如果存在,说明有一个点是它们的交集。(这段确实不好理解)

代码部分(因为有两个重构树,所以写的Class)

/*
* Author:Ehundategh
* Update:2023/10/17
* Title:P4899 [IOI2018] werewolf 狼人.cpp
* You steal,I kill
*/
#include <cstdio>
#include <iostream>
#include <algorithm>
#define MAXN 800010
#define MAXM 800010
#define LSon Node[Now].LeftS
#define RSon Node[Now].RightS
using namespace std;
int n,m,q,In1,In2,In3,In4;
template <typename T> inline void read(T &x){
int f=0;x=0;char c=getchar();
for(;!isdigit(c);c=getchar())f|=(c=='-');
for(;isdigit(c);c=getchar())x=((x<<3)+(x<<1)+(c^48));
x=f?-x:x;
}
struct edge{
int St,Ed;
}Edge[MAXM];
bool cmpman(edge a,edge b){return min(a.St,a.Ed)>min(b.St,b.Ed);}
bool cmpwolf(edge a,edge b){return max(a.St,a.Ed)<max(b.St,b.Ed);}
class President_Tree{
private:
int cnt=0;
struct node{
int Left,Right;
int LeftS,RightS;
int Sum;
}T[MAXN<<5];
public:
int Roots[MAXN];
int New_Tree(int Last,int Left,int Right,int Value){
int Root=++cnt;
T[Root].LeftS=T[Last].LeftS;
T[Root].RightS=T[Last].RightS;
T[Root].Sum=T[Last].Sum+1;
T[Root].Left=Left;T[Root].Right=Right;
int Mid=(Left+Right)/2;
if(Left!=Right){
if(Value<=Mid){T[Root].LeftS=New_Tree(T[Last].LeftS,Left,Mid,Value);}
else{T[Root].RightS=New_Tree(T[Last].RightS,Mid+1,Right,Value);}
}
return Root;
}
int Query(int Pl,int Pr,int Left,int Right){
if(T[Pr].Left>=Left&&T[Pr].Right<=Right){
return T[Pr].Sum-T[Pl].Sum;
}
else if(T[Pr].Right<Left||T[Pr].Left>Right) return 0;
else return (Query(T[Pl].LeftS,T[Pr].LeftS,Left,Right)|Query(T[Pl].RightS,T[Pr].RightS,Left,Right));
}
}President;
class Kruskal{
private:
int Fa[MAXN<<1][21],Father[MAXN<<1];
public:
struct node{
int LeftS,RightS,Left,Right,Value;
}Node[MAXN<<1];
int cnt,DFN[MAXN],cnd=0,Line[MAXN];
int Find(int a){return Father[a]==a?Father[a]:Father[a]=Find(Father[a]);}
void Merge(int a,int b,int c,int Type){
int Faa=Find(a),Fab=Find(b);
Father[Faa]=Father[Fab]=c;
Fa[Faa][0]=Fa[Fab][0]=c;
Node[c].LeftS=Faa;Node[c].RightS=Fab;
if(Type==0) Node[c].Value=min(a,b);
else Node[c].Value=max(a,b);
}
void Build(int Type){
cnt=n;
for(int i=1;i<=2*n;i++) Father[i]=i,Fa[i][0]=i;
if(Type==0) sort(Edge+1,Edge+m+1,cmpman);
else sort(Edge+1,Edge+m+1,cmpwolf);
for(int i=1;i<=m;i++){
if(Find(Edge[i].St)==Find(Edge[i].Ed)) continue;
Merge(Edge[i].St,Edge[i].Ed,++cnt,Type);
}
}
void Pre(){
for(int i=1;i<=cnt;i++){
if(Fa[i][0]==i) DFS(i);
}
}
void DFS(int Now){
Node[Now].Left=1<<30;
Node[Now].Right=0;
if(!(LSon||RSon)) {
DFN[Now]=Node[Now].Left=Node[Now].Right=++cnd;
Line[DFN[Now]]=Now;
return;
}
DFS(LSon),Node[Now].Left=min(Node[Now].Left,Node[LSon].Left),Node[Now].Right=max(Node[Now].Right,Node[LSon].Right);
DFS(RSon),Node[Now].Left=min(Node[Now].Left,Node[RSon].Left),Node[Now].Right=max(Node[Now].Right,Node[RSon].Right);
}
void Init(){
for(int i=1;i<=19;i++){
for(int j=1;j<=cnt;j++){
Fa[j][i]=Fa[Fa[j][i-1]][i-1];
}
}
}
int Jump(int Now,int Type,int Top){
for(int i=19;i>=0;i--){
if(Type==0&&Node[Fa[Now][i]].Value>=Top) Now=Fa[Now][i];
else if(Type==1&&Node[Fa[Now][i]].Value<=Top) Now=Fa[Now][i];
}
return Now;
}
}T1,T2;
void Init_President(){
for(int i=1;i<=n;i++){
President.Roots[i]=President.New_Tree(President.Roots[i-1],1,n,T2.DFN[T1.Line[i]]);
// printf("%d %d\n",i,T2.DFN[T1.Line[i]]);
}
}
bool Judge(int S,int T,int L,int R){
S=T1.Jump(S,0,L);
T=T2.Jump(T,1,R);
// printf("%d %d %d %d\n",T1.Node[S].Left-1,T1.Node[S].Right,T2.Node[T].Left,T2.Node[T].Right);
int Temp=President.Query(President.Roots[T1.Node[S].Left-1],President.Roots[T1.Node[S].Right],T2.Node[T].Left,T2.Node[T].Right);
if(Temp) return true;
else return false;
}
int main(){
read(n);read(m);read(q);
for(int i=1;i<=m;i++){
read(Edge[i].St);read(Edge[i].Ed);
Edge[i].St++;
Edge[i].Ed++;
}
T1.Build(0);T2.Build(1);
T1.Pre();T2.Pre();
T1.Init();T2.Init();
Init_President();
while(q-->0){
read(In1);read(In2);read(In3);read(In4);
In1++;In2++;In3++;In4++;
if(Judge(In1,In2,In3,In4)) puts("1");
else puts("0");
}
return 0;
}

如果觉得这篇题解让你有所收获,就点个赞吧。

P4899 [IOI2018] werewolf 狼人 题解的更多相关文章

  1. [LOJ2865] P4899 [IOI2018] werewolf 狼人

    P4899 [IOI2018] werewolf 狼人 LOJ#2865.「IOI2018」狼人,第一次AC交互题 kruskal 重构树+主席树 其实知道重构树的算法的话,难度就主要在主席树上 习惯 ...

  2. p4899 [IOI2018] werewolf 狼人

    分析 我用的主席树维护qwq 代码 #include<iostream> #include<cstdio> #include<cstring> #include&l ...

  3. [IOI2018] werewolf 狼人

    [IOI2018] werewolf 狼人 IOI2018题解 (其实原题强制在线,要用主席树) 代码: 注意: 1.下标从0~n-1 2.kruskal重构树开始有n个节点,tot从n开始,++to ...

  4. [IOI2018] werewolf 狼人 kruskal重构树,主席树

    [IOI2018] werewolf 狼人 LG传送门 kruskal重构树好题. 日常安利博客文章 这题需要搞两棵重构树出来,这两棵重构树和我们平时见过的重构树有点不同(据说叫做点权重构树?),根据 ...

  5. 题解 洛谷 P4899 【[IOI2018] werewolf 狼人】

    先考虑狼形,其只能走编号小于\(R\)的点.若将每条边赋边权为其两端点编号的较大值,然后按最小生成树的顺序构建\(Kruskal\)重构树. 那么从原图的一个点\(x\)在树上倍增,到达满足要求且深度 ...

  6. P4899 【[IOI2018] werewolf 狼人】

    感觉已经几次碰到这种类型的题目了,写篇\(Blog\)总结一下 题意: 是否存在一条\((s_i, t_i)\)的路径,满足先只走编号不超过\(L_i\)的点,再走编号不超过\(R_i\)的点 \(S ...

  7. [Luogu4899][IOI2018] werewolf 狼人

    luogu sol \(\mbox{IOI2018}\)的出题人有没有看过\(\mbox{NOI2018}\)的题目呀... \(\mbox{Kruskal}\)重构树+二维数点. 题目相当于是问你从 ...

  8. luogu P4899 [IOI2018] werewolf 狼火

    传送门 首先很显然,从人形起点出发能到的点和狼形能到终点的点都是一个联通块,如果能从起点到终点则说明这两个联通块有交 这个时候可以请出我们的克鲁斯卡尔重构树,即对原图分别建两棵重构树,一棵边权为两端点 ...

  9. [IOI2018] werewolf 狼人 [kruskal重构树+主席树]

    题意: 当你是人形的时候你只能走 \([L,N-1]\) 的编号的点(即大于等于L的点) 当你是狼形的时候你只能走 \([1,R]\) 的编号的点(即小于等于R的点) 然后问题转化成人形和狼形能到的点 ...

  10. 「IOI2018」狼人

    快咕一个月了 咕咕咕 咕咕咕咕 LOJ #2865 Luogu P4899(离线) UOJ #407(强制在线) 题意 给定一棵树和若干组询问$(S,E,L,R)$ 表示你初始在$S$,想到达$E$, ...

随机推荐

  1. qrcode模块生成二维码

    安装qrcode模块 pip install qrcode 简单使用 import qrcode data = 'hello world' img = qrcode.make(data) # 显示二维 ...

  2. ENVI实现QUAC、简化黑暗像元、FLAASH方法的遥感影像大气校正

    本文介绍基于ENVI软件,实现对Landsat 7遥感影像加以预处理与多种不同大气校正方法的操作. 目录 1 数据导入与辐射定标 2 波段合成 3 编辑头文件 4 转换文件格式 5 QUAC快速大气校 ...

  3. 浅析华为云Astro的5大关键能力技术

    摘要:本文以技术方案视角,对华为云Astro低代码平台的一些核心功能进行简要介绍. 背景介绍 低代码开发基于可视化开发的概念,结合了云原生和多终端体验技术,它可以在大多数业务场景中,帮助企业显著的提升 ...

  4. 实际上手体验maven面对冲突Jar包的加载规则

    一.问题背景 相信大家在日常的开发过程中都遇到过Jar包冲突的问题,emm,在最近处理业务需求时我也遇到了不同版本jar包冲突导致项目加载出错的问题.主要是一个完整的项目会不可避免的使用第三方的Jar ...

  5. PWM点灯

    目录 PWM脉冲宽调点灯 前言 1.什么是PWM 2.PWM的实现 3.PWM实现步骤(通用定时器) 3.1 打开定时器的时钟 3.2 配置端口 3.3 设置定时器 3.4 设置PWM 3.5 完整代 ...

  6. 【MAUI Blazor踩坑日记】3.Windows标题栏自定义颜色,运行时改变颜色

    目录 修改默认颜色 修改运行时颜色 效果图 MAUI中Windows默认的标题栏颜色是灰色的,有一点丑. 如果去掉默认的标题栏,自己画一个,可能会出现问题,也比较麻烦. 想要自定义默认标题栏的颜色,官 ...

  7. 记录一次线上服务CPU飙高问题

    2023.07.20 20:01:38线上一个服务发生了CPU过高的告警, 看告警信息当前的CPU使用率已经达到了82.65%,问题已经很严重,赶紧开始排查起来.来复盘下如何排查这类问题, 一.排查方 ...

  8. Mysql高级3-索引的结构和分类

    一.索引概述 1.1 索引的介绍 索引index:是帮助 Mysql 高效获取数据 的 有序的数据结构,在数据之外,数据库系统维护着的满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据 ...

  9. 【go语言】1.1.2 Go 语言的特性

    1. 简洁的语法 Go 语言的语法设计上非常简洁明了,没有复杂的继承和泛型,也没有异常处理,但这并不影响它的功能性和表达力.这使得 Go 语言容易学习和使用. 例如,以下是一个简单的 Go 函数,用于 ...

  10. 解决npm install 报错 'proxy' config is set properly. See: 'npm help config'

    输入以下命令 npm config set proxy null npm config set https-proxy null 之后重新安装即可 文章参考 https://blog.csdn.net ...