IOI2018 werewolf 狼人 題解

来源:https://www.cnblogs.com/Ehundategh/archive/2023/10/18/17771825.html
-Advertisement-
Play Games

IOI2018 werewolf 狼人 題解 題目描述 省流: \(n\) 個點,\(m\) 條邊,\(q\) 次詢問,對於每一次詢問,給定一個起點 \(S\) 和終點 \(T\) ,能否找到一條路徑,前半程不能走 \(0\thicksim L-1\) 這些點,後半程不能走 \(R+1\thicks ...


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

如果覺得這篇題解讓你有所收穫,就點個贊吧。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 正文 上一篇文章講解了獲取事務,並且通過獲取的connection設置只讀、隔離級別等,這篇文章講解剩下的事務的回滾和提交。最全面的Java面試網站 回滾處理 之前已經完成了目標方法運行前的事務準備工作,而這些準備工作最大的目的無非是對於程式沒有按照我們期待的那樣進行,也就是出現特定的錯誤,那麼,當 ...
  • 在之前的代碼中我們並沒有對套接字進行加密,在未加密狀態下我們所有的通信內容都是明文傳輸的,這種方式在學習時可以使用但在真正的開發環境中必須要對數據包進行加密,此處筆者將演示一種基於時間的加密方法,該加密方法的優勢是數據包每次發送均不一致,但數據包內的內容是一致的,當抓包後會發現每次傳輸的數據包密文是... ...
  • 題目:從前有一隻青蛙他想跳臺階,有n級臺階,青蛙一次可以跳1級臺階,也可以跳2級臺階;問:該青蛙跳到第n級臺階一共有多少種跳法。 當只有跳一級臺階的方法跳時,總共跳n步,共有1次跳法 當用了一次跳二級臺階的方法跳時,總共跳n-1步,共有n-1次跳法 當用了兩次跳二級臺階的方法跳時,總共跳n-2步,共 ...
  • 安裝 miniconda 1、下載安裝包 Miniconda3-py37_22.11.1-1-Linux-x86_64.sh,或者自行選擇版本 2、把安裝包上傳到伺服器上,這裡放在 /home/software 3、安裝 bash Miniconda3-py37_22.11.1-1-Linux-x8 ...
  • 1.使用 for key in dict 遍歷字典 可以使用 for key in dict 遍歷字典中所有的鍵 x = {'a': 'A', 'b': 'B'} for key in x: print(key) # 輸出結果 a b 2.使用 for key in dict.keys () 遍歷字 ...
  • 在 Python 編程中,異常是一種常見的情況,可能會導致程式中斷或產生錯誤。然而,並非所有的異常都需要立即處理,有時候我們希望忽略某些異常並繼續執行程式。本文將介紹如何在 Python 中忽略異常,並提供一些示例和註意事項。 try-except 塊: 在 Python 中,我們可以使用 try- ...
  • 前言 ElasticSearch Java API是ES官方在8.x版本推出的新java api,也可以適用於7.17.x版本的es。 本文主要參考了相關博文,自己手動編寫了下相關操作代碼,包括更新mappings等操作的java代碼。 代碼示例已上傳github。 版本 elasticsearch ...
  • GCC編譯 預處理->編譯->彙編->鏈接 預處理:頭⽂件包含、巨集替換、條件編譯、刪除註釋... 編譯:主要進⾏詞法、語法、語義分析等,檢查⽆誤後將預處理好的⽂件編譯成彙編⽂件... 彙編:將彙編⽂件轉換成 ⼆進位⽬標⽂件... 鏈接:將項⽬中的各個⼆進位⽂件+所需的庫+啟動代碼鏈接成可執⾏⽂件.. ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...