[BZOJ]1095 Hide捉迷藏(ZJOI2007)

  一道神題,兩種神做法。

?

Description

  捉迷藏 Jiajia和Wind是一對恩愛的夫妻,并且他們有很多孩子。某天,Jiajia、Wind和孩子們決定在家里玩捉迷藏游戲。他們的家很大且構造很奇特,由N個屋子和N-1條雙向走廊組成,這N-1條走廊的分布使得任意兩個屋子都互相可達。游戲是這樣進行的,孩子們負責躲藏,Jiajia負責找,而Wind負責操縱這N個屋子的燈。在起初的時候,所有的燈都沒有被打開。每一次,孩子們只會躲藏在沒有開燈的房間中,但是為了增加刺激性,孩子們會要求打開某個房間的電燈或者關閉某個房間的電燈。為了評估某一次游戲的復雜性,Jiajia希望知道可能的最遠的兩個孩子的距離(即最遠的兩個關燈房間的距離)。 我們將以如下形式定義每一種操作: C(hange) i 改變第i個房間的照明狀態,若原來打開,則關閉;若原來關閉,則打開。 G(ame) 開始一次游戲,查詢最遠的兩個關燈房間的距離。

Input

  第一行包含一個整數N,表示房間的個數,房間將被編號為1,2,3…N的整數。接下來N-1行每行兩個整數a, b,表示房間a與房間b之間有一條走廊相連。接下來一行包含一個整數Q,表示操作次數。接著Q行,每行一個操作,如上文所示。

Output

  對于每一個操作Game,輸出一個整數,表示最遠的兩個關燈房間的距離。若只有一個房間是關著燈的,輸出0;若所有房間的燈都開著,輸出-1。

Sample Input

  8
  1 2
  2 3
  3 4
  3 5
  3 6
  6 7
  6 8
  7
  G
  C 1
  G
  C 2
  G
  C 1
  G

Sample Output

  4
  3
  3
  4

HINT

  N ≤100000, M ≤500000。

?

Solution

  一道很好的裸題,可以讓你初步了解括號序列和動態點分治的用法。

  

  先說說比較好理解的動態點分治吧:

    動態點分治,顧名思義就是將點分治加上修改操作(權值修改等)并支持在線詢問。

    這里說的動態不是完全動態,至少樹的整個形態是要提前知道的。

    我們仔細想想最基本的點分治怎么做:

    在當前的分治結構里把所有的點到分治根結點的距離處理出來,每兩條不在同一棵子樹內的距離構成一條鏈。

    因此找經過分治根結點的最長的一條鏈就是到分治根結點的最長距離加上次長距離。當然這兩個距離不能在同一棵子樹內。你懂的。

    然后怎么讓這個點分治“動”起來?當然是選擇數據結構啊。

    清點一下,我們要維護的信息有:

      ①分治結構中分治根結點的某個子樹內所有的點到分治根結點的距離,目標是求最大值;

      ②由①得出的,分治結構中分治根結點的每個子樹內的距離最大值,目標是求最大值和次大值;

      ③由②得出的,每個分治結構中過分治根結點的鏈的長度,目標是求最大值(即答案)。

    都是維護單點修改求整體最值!用什么?線段樹?當然是堆啊!

    你可能會對①產生疑問,一個分治根結點難道要維護它的所有子樹的距離信息?一個結點開多個堆??

    當然不是啊!反過來想,改成維護該分治結構中所有的點到分治父節點的距離,就完美解決了上面的問題。

    除了“維護什么”,還有“怎么維護”。

    這是一個典(sang)型(bing)的維護堆套堆套堆,從最低一級的堆開始,每次堆頂有變,就要往上一級更新,小C不再贅述。

    手寫堆可能會寫得你欲仙欲死,這時候需要想辦法用上PQ。(如果你是Pascal黨當我沒說)

    PQ是無法對堆內部的節點進行修改的,所以我們需要一些經典Trick。

    用兩個堆來表示,一個用來存節點,一個用來打刪除標記。每次取top的時候把打了刪除標記的堆頂清理一下。

    取次大的就把最大pop掉再push進來即可。這些具體可以看小C的代碼。

    說完“怎么維護”,還有“為什么可以這樣維護”。

    動態點分治的時空復雜度是以點分治為基礎的,由于分治根結點都是重心。每個結點最多只會出現在log個分治結構中。

    由于所有的信息都要維護,空間和時間復雜度起步都是O(nlogn)。

    如果要開線段樹,就要動態開點,空間復雜度O(nlog2n),這時就需要注意空間上的限制了。

    所以無需注意標號和空間占用小的靈活的堆成為了很好的選擇。

    空間復雜度O(nlogn),時間復雜度O(nlog2n)。

      發張圖輕松一下~~

?

  接著小C來講講神一般的括號序列做法:

    我們先引入一個大佬的博客:http://www.shuizilong.com/house/archives/bzoj-1095-zjoi2007hide-捉迷藏/

    括號序列是什么?你只要寫過樹剖就會很熟悉。因為括號序列本身就是由dfs序的開頭和結尾組成的。

    dfs的開頭作為左括號,結尾作為右括號,節點編號緊挨著左括號。

    如下圖,可以表示為:[1[2[3][5[4]]]]。

      

    然后這樣表示有什么用呢?括號序列的作用之一就是可以配合線段樹查詢兩點之間即一條鏈上的信息。

    詢問點對(1,4)的距離,1、4之間的括號串為“[2[3][5[”;

    去掉數字:“[[][[”,再去掉匹配的括號:“[[[”,右括號代表向上,左括號代表向下,

    這也就意味著節點1向下走3步就可以走到節點4。

    所以樹上任意兩點間的距離可以用數對(a,b)表示,其中a為失配的右括號數,b為失配的左括號數,則兩點間距離為a+b。

    每一段括號序列都有它的(a,b),也就是說,只要求出兩點間的括號序列的a+b即為距離。

    現在進入正題了,如何用線段樹求a+b呢?

    考慮左(a1,b1)右(a2,b2)兩個區間合并:若b1<a2,則為(a1+a2-b1,b2);若b1>a2,則為(a1,b2+b1-a2)。

    由此我們從已知a1,b1,a2,b2可以得出以下關于新區間(a,b)的顯而易見的等式:

      ①

      ②

      ③

    我們發現,新的a+b和a-b都是由舊的a+b和a-b通過加減運算得來。

    所以設一段括號序列的a+b為plus,a-b為minus1,b-a為minus2。

    進一步,題目要我們求的是兩個黑點之間的a+b,我們同樣可以通過維護以下信息求得:

      ①sum:表示區間中兩個黑點之間的括號序列的a+b的最大值;

      ②left_plus:表示區間中一個黑點左側的括號序列的b+a的最大值;

      ③left_minus:表示區間中一個黑點左側的括號序列的b-a的最大值;

      ④right_plus:表示區間中一個黑點右側的括號序列的a+b的最大值;

      ⑤right_minus:表示區間中一個黑點右側的括號序列的a-b的最大值;

    注意上面的變量如果不存在(即黑點數不足時),都要設為-INF。

    剩下的就是各種區間的合并,“兩點之間”、“左側”、“右側”實際上都是區間,根據上面的等式可以很容易求得:

      ①

      ②

      ③

      ④

      ⑤

    然后就開開心心地寫一寫線段樹就可以了啊。

    時間復雜度O(nlogn),比動態點分治快到不知道那里去。

?

  動態點分治:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
#define INF 0x3FFFFFFF
#define MN 100005
using namespace std;
struct queue
{priority_queue <int> A,B;void push(int x) {if (x!=-INF) A.push(x);}void delet(int x) {if (x!=-INF) B.push(x);}int top(){while (!B.empty()&&A.top()==B.top()) A.pop(),B.pop();if (!A.empty()) return A.top(); else return -INF;}int two(){if (A.size()-B.size()<2) return -INF;register int x,y;x=top(); A.pop(); y=top(); A.push(x); return x+y;}
}q[MN],q1[MN],q2;
struct edge{int nex,to;}e[MN<<1];
vector <int> td[MN];
int siz[MN],hr[MN],fa[MN];
int mnz,mni,n,m,pin,gs;
bool bj[MN],hu[MN];inline int read()
{int n=0,f=1; char c=getchar();while (c<'0' || c>'9') {if(c=='-')f=-1; c=getchar();}while (c>='0' && c<='9') {n=n*10+c-'0'; c=getchar();}return n*f;
}inline void ins(int x,int y) {e[++pin]=(edge){hr[x],y}; hr[x]=pin;}
void getsiz(int x,int fat)
{siz[x]=1;for (register int i=hr[x];i;i=e[i].nex)if (e[i].to!=fat&&!bj[e[i].to])getsiz(e[i].to,x),siz[x]+=siz[e[i].to];
}
void getdp(int x,int fat,int depth,int dest)
{q[dest].push(depth);td[x].push_back(depth);for (register int i=hr[x];i;i=e[i].nex)if (e[i].to!=fat&&!bj[e[i].to])getdp(e[i].to,x,depth+1,dest);
}
void getrt(int x,int fat,int tot)
{register int i,mxz=0;for (i=hr[x];i;i=e[i].nex){if (e[i].to==fat||bj[e[i].to]) continue;getrt(e[i].to,x,tot);mxz=max(mxz,siz[e[i].to]);}mxz=max(mxz,tot-siz[x]);if (mxz<mnz) mnz=mxz,mni=x;
}void dfs(int x,int fat)
{bj[x]=true; fa[x]=fat;q1[x].push(0);for (register int i=hr[x];i;i=e[i].nex){if (bj[e[i].to]) continue;getsiz(e[i].to,x); mnz=n; getrt(e[i].to,x,siz[e[i].to]);getdp(e[i].to,x,1,mni);q1[x].push(q[mni].top());dfs(mni,x);}q2.push(q1[x].two());
}void setrev(int x)
{register int ck,nck,cck,ncck,y,i;ck=q1[x].two();if (hu[x]) q1[x].delet(0); else q1[x].push(0);nck=q1[x].two();if (ck!=nck) q2.delet(ck),q2.push(nck);for (y=x,i=td[x].size()-1;fa[y];y=fa[y],--i){ck=q[y].top();if (hu[x]) q[y].delet(td[x][i]); else q[y].push(td[x][i]);nck=q[y].top();if (ck==nck) continue;cck=q1[fa[y]].two();q1[fa[y]].delet(ck); q1[fa[y]].push(nck);ncck=q1[fa[y]].two();if (cck!=ncck) q2.delet(cck),q2.push(ncck);}
}int main()
{register int i,x,y;char c[5];n=read();for (i=1;i<n;++i){x=read(); y=read();ins(x,y); ins(y,x);}for (i=1;i<=n;++i) hu[i]=true;getsiz(1,0); mnz=n; getrt(1,0,n); dfs(mni,0);m=read(); gs=n;while (m--){scanf("%s",c);if (c[0]=='C'){x=read(); setrev(x);gs+=hu[x]?-1:1; hu[x]^=1;}else if (c[0]=='G')if (gs==0) puts("-1");else if (gs==1) puts("0");else printf("%d\n",q2.top());}
}

?

  括號序列(畫風略清奇):

#include <cstdio>
#include <algorithm>
#include <cstring>
#define l(a) (a<<1)
#define r(a) (a<<1|1)
#define O(a) (a!=-INF)
#define INF 100000007
#define MM 800005
#define MN 200005
using namespace std;
struct node{int lpl,lmi,rpl,rmi,sum;}T[MM];
struct meg{int x,y;}t[MM];
struct edge{int nex,to;}e[MN];
bool u[MN];
int kh[MN][2],hr[MN];
int dfn,n,m,pin,gs;inline int read()
{int n=0,f=1; char c=getchar();while (c<'0' || c>'9') {if(c=='-')f=-1; c=getchar();}while (c>='0' && c<='9') {n=n*10+c-'0'; c=getchar();}return n*f;
}inline void ins(int x,int y) {e[++pin]=(edge){hr[x],y}; hr[x]=pin;}
void dfs(int x,int fat)
{u[kh[x][0]=++dfn]=true;for (register int i=hr[x];i;i=e[i].nex)if (e[i].to!=fat) dfs(e[i].to,x);kh[x][1]=++dfn;
}void update(node& C,const node& A,const node& B,const meg& a,const meg& b)
{C.sum=max(A.sum,B.sum); if (O(A.lpl)&&O(B.lpl)) C.sum=max(C.sum,max(A.rpl+B.lmi,A.rmi+B.lpl));C.lpl=A.lpl;            if (O(B.lpl)) C.lpl=max(C.lpl,max(B.lpl-a.y+a.x,B.lmi+a.y+a.x));C.rpl=B.rpl;            if (O(A.lpl)) C.rpl=max(C.rpl,max(A.rpl-b.x+b.y,A.rmi+b.x+b.y));C.lmi=A.lmi;            if (O(B.lpl)) C.lmi=max(C.lmi,B.lmi+a.y-a.x);C.rmi=B.rmi;            if (O(A.lpl)) C.rmi=max(C.rmi,A.rmi+b.x-b.y);
}
inline void setin(int x) {T[x].lpl=T[x].lmi=1; T[x].rpl=T[x].rmi=0;}
inline void setout(int x) {T[x].lpl=T[x].lmi=T[x].rpl=T[x].rmi=-INF;}
void getcg(int x,int L,int R,int q)
{    if (L==R) {if (O(T[x].lpl)) setout(x); else setin(x); return;}int mid=L+R>>1;if (q<=mid) getcg(l(x),L,mid,q); else getcg(r(x),mid+1,R,q);update(T[x],T[l(x)],T[r(x)],t[l(x)],t[r(x)]);
}
void build(int x,int L,int R)
{if (L==R){T[x].sum=-INF;if (!u[L]) t[x].x=1,setout(x); else t[x].y=1,setin(x);return;}int mid=L+R>>1;build(l(x),L,mid); build(r(x),mid+1,R);t[x].x=t[l(x)].x; t[x].y=t[r(x)].y;if        (t[l(x)].y<t[r(x)].x) t[x].x+=t[r(x)].x-t[l(x)].y;else if (t[l(x)].y>t[r(x)].x) t[x].y+=t[l(x)].y-t[r(x)].x;update(T[x],T[l(x)],T[r(x)],t[l(x)],t[r(x)]);
}int main()
{register int i,x,y;char c[5];n=read();for (i=1;i<n;++i){x=read(); y=read();ins(x,y); ins(y,x);}dfs(1,0); build(1,1,n<<1); gs=n;    m=read();while (m--){scanf("%s",c);if (c[0]=='C'){x=read(); gs+=u[kh[x][0]]?-1:1;u[kh[x][0]]^=1; getcg(1,1,n<<1,kh[x][0]);}else if (c[0]=='G')if (gs==1) puts("0");else if (gs==0) puts("-1");else printf("%d\n",T[1].sum);}
}

?

Last Word

  動態點分治寫起來就像吃了那啥一樣難受,還好最后把代碼壓縮到小C容易接受的地步。

  括號序列是真的厲害,不知道以后能不能看到它更多的妙用。

轉載于:https://www.cnblogs.com/ACMLCZH/p/7465161.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/282522.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/282522.shtml
英文地址,請注明出處:http://en.pswp.cn/news/282522.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Spring4-自動裝配Beans-通過注解@Autowired在構造方法上

1.創建Maven項目,項目名稱springdemo19,如圖所示2.配置Maven,修改項目中的pom.xml文件,修改內容如下<project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation"http://mave…

15個開源的工業軟件

出品 | OSC開源社區&#xff08;ID&#xff1a;oschina2013)不同的工業流程&#xff0c;需要不同的工業軟件。此前&#xff0c;我們已經介紹了面向研發設計環節的開源軟件&#xff08;詳情查看&#xff1a;20 個開源的工業設計軟件&#xff09;&#xff0c;今天就來介紹一下面向…

PHP開發中保證接口安全

模擬客戶端請求:<?php namespace Home\Controller; use Think\Controller;class ClientController extends Controller{const TOKEN API;//模擬前臺請求服務器api接口public function getDataFromServer(){//時間戳$timeStamp time();//隨機字符串$randomStr $this ->…

MySQL遠程訪問報錯解決

2019獨角獸企業重金招聘Python工程師標準>>> 我之前的一篇博客講了MySQL配置遠程訪問的方法&#xff0c;但是可能配置了賬戶以后還是不能訪問&#xff0c;這可能是防火墻的原因&#xff0c;在CentOS里&#xff0c;我們修改一下防火墻設置就可以了 1. 進入防火墻配置…

jssdk.php

/*** Created by PhpStorm.* Date: 17/8/19* Time: 下午2:24*/ class JSSDK {private $appId;private $appSecret;public function __construct($appId, $appSecret) {$this->appId $appId;$this->appSecret $appSecret;}public function getSignPackage() {$jsapiTick…

GNU/Linux與開源文化的那些人和事

一、計算機的發明 世上本無路&#xff0c;走的人多了&#xff0c;就有了路。世上本無計算機&#xff0c;琢磨的人多了……沒有計算機&#xff0c;一切無從談起。 三個人對計算機的發明功不可沒&#xff0c;居功至偉。阿蘭圖靈&#xff08;Alan Mathison Turing&#xff09;、阿…

PHP使用PHPMailer發送郵件

1. 首先下載phpmailer插件,并將插件復制到目錄下 下載地址: http://download.csdn.net/download/m_nanle_xiaobudiu/10261269 2. home/view/user/mail_chck.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><…

python學習記錄2

一、兩個模塊&#xff08;sys和os&#xff09; 1 #!/usr/bin/env python2 # _*_ coding: UTF-8 _*_3 # Author:taoke4 import sys5 print(sys.path)#打印環境變量6 print(sys.argv[0])#當前文件相對路徑,sys.argv是一個列表&#xff0c;第一個元素為程序本身的相對路徑&#xf…

cordova-config.xml配置應用圖標

1. <icon src"res/icon/ios/browser.png"/> 2.規格&#xff1a; iphone平臺一般要求3種規格的圖片&#xff1a;1x、2x、3x&#xff0c;也是就Icon.png、Icon2x.png、Icon3x.png. 注意&#xff1a;iOS所有圖標的圓角效果由系統生成&#xff0c;給到的圖標本身不…

將 Figma 設計轉換為 .NET MAUI Graphics 代碼

原文鏈接&#xff1a;https://github.com/jsuarezruiz/figma-to-maui-graphics原文作者&#xff1a;jsuarezruiz翻譯&#xff1a;沙漠盡頭的狼(谷歌翻譯加持)&#xff0c;翻譯別扭&#xff0c;建議直接閱讀原文使用FigmaSharp.Maui.Graphics將Figma設計轉換為 .NET MAUI Graphi…

Android之上下文context

Context&#xff0c;中文直譯為“上下文”&#xff0c;SDK中對其說明如下&#xff1a; 1、它描述的是一個應用程序環境的信息&#xff0c;即上下文。 2、該類是一個抽象(abstract class)類&#xff0c;Android提供了該抽象類的具體實現類。 3、通過它我們可以獲取應用程序的資…

論壇中,無限分類的原理

1.創建數據表 CREATE TABLE category( cat_id SMALLINT unsigned not null auto_increment comment 類別id, cat_name VARCHAR(30) not null default comment 類別名稱, par_id SMALLINT unsigned not null default 0 comment 類別父id, PRIMARY KEY (cat_id) )enginemyisam …

mooc- 基本程序設計方法week1,week2

學習了第一單元我們幾本可以寫出10行左右的代碼。 week1:python編程之基本方法 1、從計算機到程序設計語言&#xff1a; 理解計算機&#xff1a;計算機是能夠根據一組指令操作數據的機器。 功能性&#xff1a;可以進行數據計算 可編程性&#xff1a;根據一系列指令來執行 計算機…

Windows 11 的 2022 更新為每個人帶來了新的東西

Windows 網站發布博客&#xff0c;宣布今天在 190 多個國家/地區推出 Windows 11 2022 更新。微軟在過去一年中對 Windows 11 進行了非常大的改進&#xff0c;感覺每個月都有一次更新。對于之前的 Windows 11&#xff0c;相信很多人在使用過程中也遇到過或大或小的問題。而一部…

goaccess_nginx日志分析工具

在控制臺分析nginx日志goaccess -f b.log生成html文件分析nginx日志vi ~/.goacce***ctime-format %Tdate-format %d/%b/%Ylog-format %h %^[%d:%t %^] "%r" %s %b "%R" "%u"各參數詳解&#xff1a; man goaccess或Nginx Variable …

HTML5 Canvas 繪制六葉草

注意&#xff1a; context.arc(橫坐標,縱坐標,弧半徑,起始角度,終止角度,逆順時針);這個函數挺難用&#xff0c;主要原因是最后參數和角度的關系。不管文檔怎么說&#xff0c;按我的實際經驗&#xff0c;逆順時針false時&#xff0c;是逆時針旋轉&#xff1b;逆順時針true時&am…

tp框架中執行事務

function tran() {//定義事務成功失敗的標志$mark true;//1. 實例化模型$model D(student);//2. 開啟事務處理$model->startTrans();//3. ls減少2000$sql "update student set moneymoney-2000 where unamels";$result $model->execute($sql);//判斷sql執行…

哪些聽起來像段子一樣的故事?

杭州海底世界&#xff0c;一個小走廊兩邊都是各種爬行動物展覽。有兩只蜥蜴當時是這個樣子人還年輕&#xff0c;還比較猥瑣&#xff0c;看到一個趴在另一個身上就覺得在做什么羞羞的事。于是就拍下來&#xff0c;發到群里&#xff0c;然后說了句交配中。然后一天就光拍照&#…

Event 事件 - 基礎

事件驅動三要素 事件源&#xff1a;即觸發事件的元素 事件&#xff1a;被JavaScript檢測到的行為。例如&#xff1a; 鼠標點擊 鍵盤按鍵 選輸入框 事件處理函數&#xff1a;事件發生時要進行的操作&#xff0c;又叫做“事件句柄”或“事件監聽器” 事件分類&#xff1a; 鼠標事…

String 與 StringBuilder 區別與用法

String用final修飾&#xff0c;實際上是不可更改的。我們平常用的“”來連接&#xff0c;實際執行過程中是將原字符串連接之后生成新的對象重新賦值給這個名字的字符串。Testpublic void myStrTest(){String s "str_s";System.out.println(s);String ss s.toUpperC…