IOI2018 werewolf 狼人 题解

这篇具有很好参考价值的文章主要介绍了IOI2018 werewolf 狼人 题解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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;
}

如果觉得这篇题解让你有所收获,就点个赞吧。文章来源地址https://www.toymoban.com/news/detail-710828.html

到了这里,关于IOI2018 werewolf 狼人 题解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 微信小程序狼人杀游戏代码及步骤

    一、准备工作: 1. 安装微信开发者工具,创建小程序项目; 2. 准备游戏角色图片; 3. 准备游戏背景音乐; 二、实现步骤: 1. 创建游戏页面,添加游戏角色图片,添加游戏背景音乐; 2. 创建游戏角色类,定义游戏角色属性,如角色名称、角色图片、角色能力等; 3. 创建游戏

    2024年02月09日
    浏览(192)
  • photoshopCC 2018入门学习

    图层:一张张透明的纸叠加在一块的显示效果,放在最上面的图层,优先级是最高的 ( Shift + 指定快捷键 可以进行工具内部的切换) 移动v 选区m(取消选区( Shift + D ) 1.矩形选区选择正方形选区( Shift + 鼠标左键 ) 2.标尺快捷键( Ctrl + R )辅助定位作用,再利用Alt键进行从中心

    2024年02月05日
    浏览(19)
  • VUE路由(2018)

    在当前的前端开发中,路由是一个很重要的概念,尤其是针对单页面app,因为通过路由可以实现不跳转加载刷新。比如angular应用,vue应用。 VUE路由语法: 1.首先,安装方法和angular路由类似,可以单独安装,也可以直接使用VUE-cli集成使用。 2.vue接收路由的渲染输出的标签是

    2024年02月10日
    浏览(18)
  • web:[HCTF 2018]WarmUp

    题目 点进页面,页面只有一张滑稽脸,没有其他的提示信息 查看网页源代码,发现source.php,尝试访问一下 跳转至该页面,页面显示为一段php代码,需要进行代码审计 先访问hint.php看看有没有别的提示信息 根据提示信息,进行目录穿越可获得flag,这里flag写了四次,可能使用

    2024年02月07日
    浏览(44)
  • P4491 [HAOI2018] 染色

    传送门:洛谷 写本题需要知道一个前置知识: 假设恰好选 k k k 个条件的方案数为 f ( k ) f(k) f ( k ) ;先钦定选 k k k 个条件,其他条件无所谓的方案数为 g ( k ) g(k) g ( k ) 那么存在这样的一个关系: g ( k ) = ∑ i = k n C i k f ( i ) g(k)=sum_{i=k}^nC_{i}^kf(i) g ( k ) = ∑ i = k n ​ C i k ​ f ( i ) 上述

    2024年02月07日
    浏览(36)
  • [BUUCTF 2018]Online Tool

    打开题目就是源码,审一下。  escapeshellarg  — 把字符串转码为可以在 shell 命令里使用的参数 功能  :escapeshellarg() 将给字符串增加一个单引号并且能引用或者转码任何已经存在的单引号,这样以确保能够直接将一个字符串传入 shell 函数,shell 函数包含 exec(), system() 执行运

    2024年02月02日
    浏览(39)
  • 【安全】原型链污染 - Hackit2018

    目录 准备工作 解题 代码审计 Payload         将这道题所需依赖模块都安装好后          运行一下,然后可以试着访问一下,报错是因为里面没内容而已,不影响,准备工作就做好了 代码审计  解题关键就在这里,将请求体里面的row,col,data都是我们post传进去的可控的

    2024年02月11日
    浏览(44)
  • LAHeart2018左心房分割实战

    2018 Atrial Segmentation Challenge The Left Atrium (LA) MR dataset from the Atrial Segmentation Challenge 数据集下载地址:Data – 2018 Atrial Segmentation Challenge (cardiacatlas.org) 数据集结构: 一共有154例包含 心房颤动 的 3D MRI 图像 分为训练集(Training Set)和测试集(Testing Set,已开源),数据集下每个文

    2024年02月06日
    浏览(15)
  • [网鼎杯 2018]Fakebook1

    打开题目 我们先随便join一下 这里起初我的四个框都是随便填1的,但是发现注册不了,几经尝试以后发现blog那个框里面必须填一个类似什么网页的域名,例如我乱写的qqq.com,bai.com等 所以我最后乱填了个baidu.com就注册成功了 其余都是乱填1 加入成功 点击我们的用户名 发现了

    2024年02月06日
    浏览(41)
  • 2018年软件评测师真题精选

    1、软件测试的对象不包括(51)。 (51)A.代码 B.软件测试文档 C.相关文件数据 D.开发人员 【答案】D 【解析】 2、集成测试的集成方式不包括(52)。 (52)A.一次性集成 B.自中间到两端集成 C.自顶向下集成 D.自底向上集成 【答案】B 【解析】 集成测试的集成方式包括:一次

    2024年02月09日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包