[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)$,可以通过
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }