題目描述html
Jiajia和Wind是一對恩愛的夫妻,而且他們有不少孩子。某天,Jiajia、Wind和孩子們決定在家裏玩捉迷藏遊戲。他們的家很大且構造很奇特,由N個屋子和N-1條雙向走廊組成,這N-1條走廊的分佈使得任意兩個屋子都互相可達。c++
遊戲是這樣進行的,孩子們負責躲藏,Jiajia負責找,而Wind負責操縱這N個屋子的燈。在起初的時候,全部的燈都沒有被打開。每一次,孩子們只會躲藏在沒有開燈的房間中,可是爲了增長刺激性,孩子們會要求打開某個房間的電燈或者關閉某個房間的電燈。爲了評估某一次遊戲的複雜性,Jiajia但願知道可能的最遠的兩個孩子的距離(即最遠的兩個關燈房間的距離)。git
咱們將以以下形式定義每一種操做:ide
輸入格式:優化
第一行包含一個整數N,表示房間的個數,房間將被編號爲1,2,3…N的整數。ui
接下來N-1行每行兩個整數a, b,表示房間a與房間b之間有一條走廊相連。編碼
接下來一行包含一個整數Q,表示操做次數。接着Q行,每行一個操做,如上文所示。spa
輸出格式:code
對於每個操做Game,輸出一個非負整數到hide.out,表示最遠的兩個關燈房間的距離。若只有一個房間是關着燈的,輸出0;若全部房間的燈都開着,輸出-1。htm
對於20%的數據, N ≤50, M ≤100;
對於60%的數據, N ≤3000, M ≤10000; 對於100%的數據, N ≤100000, M ≤500000。
題解
神仙般的操做……
咱們先假設有以上這麼一棵樹(圖醜勿介)
進行先序遍歷,獲得$[A[B[E][F[H][I]]][C][D[G]]]$
再把全部字母去掉$[ [ [ ] [ [ ] [ ] ] ] [ ] [ [ ] ] ]$
這就是這一棵樹的括號編碼(本質是dfs獲得的)
花了這麼大功夫找,但這玩意兒到底有什麼用呢?
咱們考慮兩個節點,E和G
取出他們之間的那段括號編碼$] [ [ ] [ ] ] ] [ ] [ [$
再將全部匹配的括號去掉,獲得$] ] [ [$
咱們看到了兩個$]$和兩個$[$
再回到樹上,咱們發現E向上走兩步,再向下走兩步就到達了G
因而發現括號序列能夠很方便地維護點與點之間的距離
能不能進一步優化呢?
咱們發現,對於距離而言,匹配的括號是沒有任何意義的
並且,因爲距離只須要記錄數字,因此維護括號也是沒有意義的,只要有編碼就行,能夠用一個二元組$(a,b)$來描述它,表示有a個$]$和b個$[$
因此,若是有兩個點P和Q,若是介於P和Q之間的括號編碼表示爲$(a,b)$,則P和Q在樹上的距離就是a+b
是否是很方便啊~\(≧▽≦)/~啦啦啦
可是如今問題又來了,怎麼維護編碼呢?
若是能夠經過左邊一半的信息和右邊一半的信息,從而獲得整段編碼的信息,就能夠用咱們熟悉的線段樹來維護了
咱們能夠進行以下的分析
考慮對於兩段括號編碼$s1(a,b)$和$s2(c,d)$,他們合併起來能夠獲得$s(x,y)$
注意到$s1$和$s2$合併起來時會產生$min(b,c)$的匹配括號,合併後他們會被抵消掉
因而
當 $b<c$ 時第一段 [ 就被消完了,兩段 $]$ 連在一塊兒,例如:
$] ] [ [ + ] ] ] [ [ = ] ] ] [ [$
當 $b>=c$ 時第二段 ] 就被消完了,兩段 $[ $連在一塊兒,例如:
$] ] [ [ [ + ] ] [ [ = ] ] [ [ [$
因而就獲得了幾個十分有用的結論
當 $b<c$ 時,$(x,y) = (a-b+c,d)$
當 $b>=c$ 時,$(x,y) = (a,b-c+d)$
因而就能夠用線段樹維護整棵樹的括號編碼~\(≧▽≦)/~啦啦啦
題目所要求維護的,是$max\{a+b|s'(a,b)是s的一個子串,且s'位於兩黑點之間\}$,咱們將這個值表示爲$dis(s)$
咱們先根據上面的兩條結論,獲得幾個推論
$①x+y=a+d+|b-c|=max((a+b-c+d),(a-b+c+d))$
$②x-y=a-b+c-d$
$③y-x=b-a+d-c$
由①式咱們能夠發現,要維護$dis(s)$,要維護四個值$a+b,d-c,a-b,d+c$
又爲了保證$s'$在兩個黑點之間,因此要加上一些限制
因而定義出以下四個參數
$rightplus:max(a+b),s'是s的一個前綴且s緊接在一個黑點以後$
$rightminus:max(a-b),s'是s的一個前綴且s緊接在一個黑點以後$
$leftplus:max(a+b),s'是s的一個後綴且一個黑點緊接在s以後$
$leftminus:max(b-a),s'是s的一個後綴且一個黑點緊接在s以後$
因而咱們就能夠用左右兩半的狀態轉移到一整段的狀態啦
仍是考慮$s(x,y),s1(a,b),s2(c,d)$
$(x,y)=b<c?(a-b+c,d):(a,b-c+d)$
$dis(s)=max(dis(s1),dis(s2),rightplus(s1)+leftminus(s2),rightminus(s1)+leftplus(s2))$
(把四個參數的值帶入上面的等式很容易發現這是正確的)
而後再來考慮如何求出四個參數呢?
$rightplus(s)=max(rightplus(s1)-c+d,rightminus(s1)+c+d,rightplus(s2))$
$rightminus(s)=max(rightminus(s1)+c-d,rightminus(s2))$
$leftplus(s)=max(leftplus(s2)-b+a,left_minus(s1)+b+a,leftplus(s1))$
$leftminus(s)=max(leftminus(s2)+b-a,leftminus(s1))$
而後就能夠用線段樹處理整個括號編碼了
實際實現的時候還有一些小細節要注意
咱們爲了實現更方便,最好仍是在編碼時加入括號
對於底層結點,若是對應字符是一個括號或者一個白點,那 麼right_plus、right_minus、left_plus、left_minus、dis 的值就都是 -inf;若是對應字符是一個黑點,那麼 right_plus、right_minus、left_plus、left_minus 都是 0,dis 是-inf。
具體細節能夠參見代碼,註解比較詳細(主要是由於本身照着打了一遍也不太看得懂代碼……)
1 //minamoto 2 #include<bits/stdc++.h> 3 #define N 100005 4 #define inf 0x3f3f3f3f 5 using namespace std; 6 inline int read(){ 7 #define num ch-'0' 8 char ch;bool flag=0;int res; 9 while(!isdigit(ch=getchar())) 10 (ch=='-')&&(flag=true); 11 for(res=num;isdigit(ch=getchar());res=res*10+num); 12 (flag)&&(res=-res); 13 #undef num 14 return res; 15 } 16 int ver[N<<1],Next[N<<1],head[N]; 17 int v[N*3],pos[N],c[N]; 18 int n,q,cnt,tot,black; 19 struct seg{ 20 int l,r,l1,l2,r1,r2,c1,c2,dis; 21 void init(int x){ 22 dis=-inf; 23 c1=c2=0; 24 if(v[x]==-1) c2=1; 25 if(v[x]==-2) c1=1; 26 /*c2爲失配左括號,c1爲失配右括號 27 爲左括號,c2=1;爲右括號,c1=1*/ 28 if(v[x]>0&&c[v[x]]) l1=l2=r1=r2=0; 29 else l1=l2=r1=r2=-inf; 30 /*爲黑點,l_plus,l_minus,r_plus,r_minus全爲0 31 爲白點或括號,全爲1*/ 32 } 33 }a[N*12]; 34 inline int max(int a,int b,int c){return max(a,max(b,c));} 35 void add(int u,int v){ 36 ver[++tot]=v,Next[tot]=head[u],head[u]=tot; 37 ver[++tot]=u,Next[tot]=head[v],head[v]=tot; 38 } 39 void dfs(int u,int fa){ 40 v[++cnt]=-1; 41 v[++cnt]=u; 42 pos[u]=cnt; 43 for(int i=head[u];i;i=Next[i]) 44 if(ver[i]!=fa) dfs(ver[i],u); 45 v[++cnt]=-2; 46 /*進入加左括號,離開加右括號*/ 47 } 48 inline void merge(seg &s,seg s1,seg s2){ 49 /*r1=max(a+b),r2=max(a-b){s1(a,b)是s前綴且s1緊接在一個黑點以後} 50 l1=max(a+b),l2=max(b-a){s2(a,b)是s後綴且s2緊接在一個黑點以前}*/ 51 int a=s1.c1,b=s1.c2,c=s2.c1,d=s2.c2; 52 s.dis=max(s1.dis,s2.dis); 53 s.dis=max(s.dis,s1.r1+s2.l2,s1.r2+s2.l1); 54 /*s.dis=max(s1.dis,s2.dis,a1+b1-a2+b2,a1-b1+a2+b2)*/ 55 b<c?(s.c1=a-b+c,s.c2=d):(s.c1=a,s.c2=b-c+d); 56 s.r1=max(s2.r1,s1.r1-c+d,s1.r2+c+d); 57 /*a+b=max(a1-b1+a2+b2,a1+b1+b2-a2)*/ 58 s.r2=max(s2.r2,s1.r2+c-d); 59 /*a-b=a1-b1+a2-b2*/ 60 s.l1=max(s1.l1,s2.l1-b+a,s2.l2+b+a); 61 /*同上*/ 62 s.l2=max(s1.l2,s2.l2+b-a); 63 /*b-a=b2-a2+b1-a1*/ 64 } 65 void build(int p,int l,int r){ 66 a[p].l=l,a[p].r=r; 67 if(l==r){ 68 a[p].init(l); 69 return; 70 } 71 int mid=(l+r)>>1; 72 build(p<<1,l,mid); 73 build(p<<1|1,mid+1,r); 74 merge(a[p],a[p<<1],a[p<<1|1]); 75 } 76 void modify(int p,int x){ 77 int l=a[p].l,r=a[p].r; 78 if(l==r){a[p].init(l);return;} 79 int mid=(l+r)>>1; 80 if(x<=mid) modify(p<<1,x); 81 else modify(p<<1|1,x); 82 merge(a[p],a[p<<1],a[p<<1|1]); 83 } 84 int main(){ 85 //freopen("testdata.in","r",stdin); 86 black=n=read(); 87 for(int i=1;i<=n;++i) c[i]=1; 88 for(int i=1;i<n;++i){ 89 int u=read(),v=read(); 90 add(u,v); 91 } 92 dfs(1,0); 93 build(1,1,cnt); 94 q=read(); 95 while(q--){ 96 char s[10]; 97 scanf("%s",s); 98 if(s[0]=='C'){ 99 int x=read(); 100 if(c[x]) --black; 101 else ++black; 102 c[x]^=1; 103 modify(1,pos[x]); 104 } 105 else{ 106 if(!black) puts("-1"); 107 else if(black==1) puts("0"); 108 else printf("%d\n",a[1].dis); 109 } 110 } 111 return 0; 112 }