1. 程式人生 > 其它 >[loj6734]圖上的遊戲

[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