P4899 [IOI2018] werewolf 狼人 题解
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 狼人 题解的更多相关文章
- [LOJ2865] P4899 [IOI2018] werewolf 狼人
P4899 [IOI2018] werewolf 狼人 LOJ#2865.「IOI2018」狼人,第一次AC交互题 kruskal 重构树+主席树 其实知道重构树的算法的话,难度就主要在主席树上 习惯 ...
- p4899 [IOI2018] werewolf 狼人
分析 我用的主席树维护qwq 代码 #include<iostream> #include<cstdio> #include<cstring> #include&l ...
- [IOI2018] werewolf 狼人
[IOI2018] werewolf 狼人 IOI2018题解 (其实原题强制在线,要用主席树) 代码: 注意: 1.下标从0~n-1 2.kruskal重构树开始有n个节点,tot从n开始,++to ...
- [IOI2018] werewolf 狼人 kruskal重构树,主席树
[IOI2018] werewolf 狼人 LG传送门 kruskal重构树好题. 日常安利博客文章 这题需要搞两棵重构树出来,这两棵重构树和我们平时见过的重构树有点不同(据说叫做点权重构树?),根据 ...
- 题解 洛谷 P4899 【[IOI2018] werewolf 狼人】
先考虑狼形,其只能走编号小于\(R\)的点.若将每条边赋边权为其两端点编号的较大值,然后按最小生成树的顺序构建\(Kruskal\)重构树. 那么从原图的一个点\(x\)在树上倍增,到达满足要求且深度 ...
- P4899 【[IOI2018] werewolf 狼人】
感觉已经几次碰到这种类型的题目了,写篇\(Blog\)总结一下 题意: 是否存在一条\((s_i, t_i)\)的路径,满足先只走编号不超过\(L_i\)的点,再走编号不超过\(R_i\)的点 \(S ...
- [Luogu4899][IOI2018] werewolf 狼人
luogu sol \(\mbox{IOI2018}\)的出题人有没有看过\(\mbox{NOI2018}\)的题目呀... \(\mbox{Kruskal}\)重构树+二维数点. 题目相当于是问你从 ...
- luogu P4899 [IOI2018] werewolf 狼火
传送门 首先很显然,从人形起点出发能到的点和狼形能到终点的点都是一个联通块,如果能从起点到终点则说明这两个联通块有交 这个时候可以请出我们的克鲁斯卡尔重构树,即对原图分别建两棵重构树,一棵边权为两端点 ...
- [IOI2018] werewolf 狼人 [kruskal重构树+主席树]
题意: 当你是人形的时候你只能走 \([L,N-1]\) 的编号的点(即大于等于L的点) 当你是狼形的时候你只能走 \([1,R]\) 的编号的点(即小于等于R的点) 然后问题转化成人形和狼形能到的点 ...
- 「IOI2018」狼人
快咕一个月了 咕咕咕 咕咕咕咕 LOJ #2865 Luogu P4899(离线) UOJ #407(强制在线) 题意 给定一棵树和若干组询问$(S,E,L,R)$ 表示你初始在$S$,想到达$E$, ...
随机推荐
- 聊聊Excel解析:如何处理百万行EXCEL文件
一.引言 Excel表格在后台管理系统中使用非常广泛,多用来进行批量配置.数据导出工作.在日常开发中,我们也免不了进行Excel数据处理. 那么,如何恰当地处理数据量庞大的Excel文件,避免内存溢出 ...
- Django容易被遗忘却无比重要的框架默认文件介绍及使用方法
在Python Web开发领域,Django框架的地位犹如璀璨的明星,其全面.高效和安全的特性使其在全球范围内广受欢迎.本文将全面解析Django框架的默认文件,并深入探讨每个文件及其组成的意义和用途 ...
- Python将大的csv文件拆分多个小的csv文件
#ecoding=utf-8 import os import time # 2019/9/8 将大的csv文件拆分多个小的csv文件 def mkSubFile(lines, head, srcNa ...
- sql注入-基于pikachu靶场学习之SQL注入
sql注入(pikachu靶场学习之SQL注入) 环境准备 环境:wmware,安装两台win10的虚拟机在服务器上部署服务,在客户机上进行安全测试 声明:不涉及互联网上的资源,学习都在内网完成,一切 ...
- 2021-7-12 VUE的组件认识
VUE组件简单实例 <!DOCTYPE html> <html> <head> <title> </title> </head> ...
- ENVI、ERDAS计算Landsat 7地表温度:单窗算法实现
本文介绍基于ENVI与ERDAS软件,对Landsat 7遥感影像数据加以单窗算法的地表温度(LST)反演操作. 目录 1 原理部分与前期操作准备 1.1 图像预处理 1.2 植被指数反演 1.3 单 ...
- pandas 删除重复项
使用如下函数: drop_duplicates 具体示例如下: import pandas as pd # 建立一个dataframe数据 df = pd.DataFrame({'k1':['one' ...
- Json 基于类 Newtonsoft.Json.Linq.JToken 的应用简介【C# 基础】
〇.前言 在日常开发中,对于 Json 的使用还是比较频繁的,特别是 Json 对象和字符串或者实体对象之间的转换. 虽然几乎天天用,但是总是感觉没那么明了,今天结合微软的 Newtonsoft.Js ...
- Java安全之Webshell免杀
Java安全之Webshell免杀 当遇到文件上传时,如果网站存在查杀软件,我们上传的一句话木马会被直接秒杀,这时候就需要做一下免杀,绕过查杀软件的检测. 思路 我的想法是先拆分,然后分别检验那些语句 ...
- MySql Workbench 迁移工具 migration 提示缺少pyodbc 2.1.8 的解决方法
想把公司的数据库转到MySQL,所以想装个MySQL测试,发现新版的MySQL(8.0.34)默认安装还是有不少问题, 一.譬如表.字段大小写的问题: lower_case_table_names=0 ...