[loj6734]圖上的遊戲
考慮原圖是一條鏈的情況——
思路:隨機一個點$x$,將其所在段(邊集)再劃分為兩段,重複此過程即可得到該鏈
實現上,(從左到右)維護每一段的左端點和邊集,二分找到最後一個刪除後$x$到根不連通的段,那麼其即是$x$所在段,再暴力列舉段中每一條邊劃分即可
前者二分顯然為$o(n\log n)$,後者即是一個類似於sort/treap的結構,總期望次數為$o(n\log n)$
最終,總查詢次數為$o(n\log n)$,可以通過
考慮原圖是一棵樹的情況——
思路:以0為根建樹,求出一個鏈剖分的結果(鏈上儲存點集、邊集和鏈頂父親所在的鏈),再利用鏈的做法求出鏈上點和邊具體的順序,最後將所有鏈組合即可得到該樹
實現上,根據思路分為兩部分(求鏈剖分和求原樹):
求鏈剖分時,對每一條鏈維護邊集、部分點集(僅考慮已經加入的點)和鏈頂父親所在的鏈,並按照剖分順序依次編號,那麼加入一個點$x$,按以下方式處理——
找到剩餘邊中在$x$到根路徑上的邊,這可以通過不斷二分實現(即找到第一條滿足刪除後$x$到根不連通的邊,直接二分不具備單調性,那麼將其字首均刪除即可),接下來有兩種情況:
1.不存在這樣的邊,即$x$在某條鏈上,二分找到編號最大的鏈滿足刪除後$x$到根不連通,那麼其即是$x$所在鏈,將$x$加入該鏈的點集即可
2.存在這樣的邊,這些邊必然是$x$到根路徑上的一個字首,即得到了一條新的鏈,而此時1中的二分即會求出該鏈鏈頂父親所在的鏈
(這裡直接二分同樣不具備單調性,將其後綴均刪除即可)
求原樹時,對每一條鏈在其鏈頂的父親所在的鏈中二分找到具體的父親即可
除了求鏈剖分中"不斷二分/分治"外,其餘均為直接二分顯然為$o(n\log n)$,前者考慮每條邊至多被得到一次,進而均攤總詢問次數為$o(n\log n)$
最終,總查詢次數為$o(n\log n)$,可以通過
結合上面兩個做法,來考慮原問題——
思路:求出一棵生成樹,再求出所有非樹邊
實現上,根據思路同樣分為兩部分(求生成樹和非樹邊):
求生成樹時,可以參考樹的做法,唯一不同的即在於$x$到根路徑並不唯一,那麼改為找到第一條滿足將其以及其之前的邊均刪除後$x$到根不連通的邊即可(這與之前的二分實現上一樣)
(可以理解為從前往後儘量刪去當前邊,並通過二分優化此過程)
另外,當前的非樹邊預設已經被刪除,下同
求非樹邊時,按dfs序從後往前考慮點$x$,一條非樹邊以$x$為一個端點當且僅當將$x$到父親的邊刪除、將這條邊加入後$x$到根不連通,同樣可以二分實現
找到邊後,還需要求出該邊另一個端點,也即求dfs序最大的點滿足將其和$x$到父親的邊刪除、將這條邊加入後$x$到根仍連通,同樣也可以二分實現
(這裡的兩個二分同樣不具備單調性,將其字首/字尾均加入/刪除即可)
重複此過程也即可得到所有非樹邊,詢問次數的分析與之前類似
最終,總查詢次數為$o(m\log n)$,可以通過
1 #include<bits/stdc++.h> 2 #include"graph.h" 3 using namespace std; 4 #define N 605 5 #define pii pair<int,int> 6 int n,m,t,vis[N],fa[N],Fa[N],dfn[N]; 7 vector<int>Vis,v[N],e[N]; 8 vector<pii>ans; 9 void clear(int p){ 10 for(int i=0;i<m;i++)Vis[i]=p; 11 } 12 void init_tree(){ 13 clear(0); 14 for(int i=0;i<m;i++) 15 if (vis[i]==1)Vis[i]=1; 16 } 17 int find1(int k){ 18 vector<int>v; 19 for(int i=0;i<m;i++) 20 if (!vis[i])v.push_back(i); 21 clear(1); 22 for(int i=0;i<v.size();i++)Vis[v[i]]=0; 23 if (query(k,Vis))return -1; 24 int l=0,r=(int)v.size()-1; 25 while (l<r){ 26 int mid=(l+r>>1); 27 clear(1); 28 for(int i=0;i<=mid;i++)Vis[v[i]]=0; 29 if (query(k,Vis))l=mid+1; 30 else r=mid; 31 } 32 return v[l]; 33 } 34 int find2(int k){ 35 init_tree(); 36 for(int i=1;i<t;i++) 37 for(int j=0;j<e[i].size();j++)Vis[e[i][j]]=0; 38 if (query(k,Vis))return 0; 39 int l=1,r=t-1; 40 while (l<r){ 41 int mid=(l+r+1>>1); 42 init_tree(); 43 for(int i=mid;i<t;i++) 44 for(int j=0;j<e[i].size();j++)Vis[e[i][j]]=0; 45 if (query(k,Vis))r=mid-1; 46 else l=mid; 47 } 48 return l; 49 } 50 int find3(int k,int x){ 51 int l=0,r=(int)e[k].size()-1; 52 while (l<r){ 53 int mid=(l+r+1>>1); 54 init_tree(); 55 Vis[e[k][mid]]=0; 56 if (query(x,Vis))r=mid-1; 57 else l=mid; 58 } 59 return v[k][l]; 60 } 61 int find4(int k){ 62 vector<int>v; 63 for(int i=0;i<m;i++) 64 if (!vis[i])v.push_back(i); 65 init_tree(); 66 for(int i=0;i<v.size();i++)Vis[v[i]]=1; 67 Vis[Fa[k]]=0; 68 if (!query(k,Vis))return -1; 69 int l=0,r=(int)v.size()-1; 70 while (l<r){ 71 int mid=(l+r>>1); 72 init_tree(); 73 for(int i=0;i<=mid;i++)Vis[v[i]]=1; 74 Vis[Fa[k]]=0; 75 if (query(k,Vis))r=mid; 76 else l=mid+1; 77 } 78 return v[l]; 79 } 80 int find5(int k,int x){ 81 int l=1,r=n; 82 while (l<r){ 83 int mid=(l+r+1>>1); 84 init_tree(); 85 for(int i=mid;i<=n;i++)Vis[Fa[dfn[i]]]=0; 86 Vis[x]=1; 87 if (query(k,Vis))r=mid-1; 88 else l=mid; 89 } 90 return dfn[l]; 91 } 92 void calc(int k){ 93 vector<int>v0,fa[N]; 94 random_shuffle(v[k].begin()+1,v[k].end()); 95 v0.push_back(v[k][0]); 96 for(int i=0;i<e[k].size();i++)fa[v[k][0]].push_back(e[k][i]); 97 for(int i=1;i<v[k].size();i++){ 98 init_tree(); 99 int l=0,r=(int)v0.size()-1; 100 while (l<r){ 101 int mid=(l+r+1>>1); 102 init_tree(); 103 for(int j=0;j<fa[v0[mid]].size();j++)Vis[fa[v0[mid]][j]]=0; 104 if (query(v[k][i],Vis))r=mid-1; 105 else l=mid; 106 } 107 vector<int>e0; 108 for(int j=0;j<fa[v0[l]].size();j++){ 109 init_tree(); 110 Vis[fa[v0[l]][j]]=0; 111 if (query(v[k][i],Vis))e0.push_back(fa[v0[l]][j]); 112 else fa[v[k][i]].push_back(fa[v0[l]][j]); 113 } 114 fa[v0[l]]=e0; 115 v0.insert(v0.begin()+l,v[k][i]); 116 } 117 v[k]=v0,e[k].clear(); 118 for(int i=0;i<v[k].size();i++)e[k].push_back(fa[v0[i]][0]); 119 } 120 vector<pii> solve(int nn,int mm){ 121 srand(time(0)); 122 n=nn,m=mm; 123 for(int i=0;i<m;i++){ 124 Vis.push_back(0); 125 ans.push_back(make_pair(0,0)); 126 } 127 for(int i=1;i<n;i++){ 128 t++; 129 while (1){ 130 int x=find1(i); 131 if (x<0)break; 132 vis[x]=1,e[t].push_back(x); 133 } 134 int pos=find2(i); 135 if (e[t].empty())t--,v[pos].push_back(i); 136 else fa[t]=pos,v[t].push_back(i); 137 } 138 for(int i=1;i<=t;i++)calc(i); 139 for(int i=1;i<=t;i++) 140 if (fa[i])fa[i]=find3(fa[i],v[i][0]); 141 dfn[0]=1,dfn[1]=0; 142 for(int i=1;i<=t;i++) 143 for(int j=0;j<v[i].size();j++){ 144 Fa[v[i][j]]=e[i][j]; 145 dfn[++dfn[0]]=v[i][j]; 146 } 147 for(int i=1;i<=t;i++){ 148 ans[e[i][0]]=make_pair(fa[i],v[i][0]); 149 for(int j=1;j<v[i].size();j++)ans[e[i][j]]=make_pair(v[i][j-1],v[i][j]); 150 } 151 for(int i=dfn[0];i>1;i--) 152 while (1){ 153 int x=find4(dfn[i]); 154 if (x<0)break; 155 vis[x]=-1,ans[x]=make_pair(dfn[i],find5(dfn[i],x)); 156 } 157 return ans; 158 }View Code