[loj6734]图上的游戏

考虑原图是一条链的情况——

思路:随机一个点$x$,将其所在段(边集)再划分为两段,重复此过程即可得到该链

实现上,(从左到右)维护每一段的左端点和边集,二分找到最后一个删除后$x$到根不连通的段,那么其即是$x$所在段,再暴力枚举段中每一条边划分即可

前者二分显然为$o(nlog n)$,后者即是一个类似于sort/treap的结构,总期望次数为$o(nlog n)$

最终,总查询次数为$o(nlog n)$,可以通过

考虑原图是一棵树的情况——

思路:以0为根建树,求出一个链剖分的结果(链上存储点集、边集和链顶父亲所在的链),再利用链的做法求出链上点和边具体的顺序,最后将所有链组合即可得到该树

实现上,根据思路分为两部分(求链剖分和求原树):

求链剖分时,对每一条链维护边集、部分点集(仅考虑已经加入的点)和链顶父亲所在的链,并按照剖分顺序依次编号,那么加入一个点$x$,按以下方式处理——

找到剩余边中在$x$到根路径上的边,这可以通过不断二分实现(即找到第一条满足删除后$x$到根不连通的边,直接二分不具备单调性,那么将其前缀均删除即可),接下来有两种情况:

1.不存在这样的边,即$x$在某条链上,二分找到编号最大的链满足删除后$x$到根不连通,那么其即是$x$所在链,将$x$加入该链的点集即可

2.存在这样的边,这些边必然是$x$到根路径上的一个前缀,即得到了一条新的链,而此时1中的二分即会求出该链链顶父亲所在的链

(这里直接二分同样不具备单调性,将其后缀均删除即可)

求原树时,对每一条链在其链顶的父亲所在的链中二分找到具体的父亲即可

除了求链剖分中"不断二分/分治"外,其余均为直接二分显然为$o(nlog n)$,前者考虑每条边至多被得到一次,进而均摊总询问次数为$o(nlog n)$

最终,总查询次数为$o(nlog n)$,可以通过

结合上面两个做法,来考虑原问题——

思路:求出一棵生成树,再求出所有非树边

实现上,根据思路同样分为两部分(求生成树和非树边):

求生成树时,可以参考树的做法,唯一不同的即在于$x$到根路径并不唯一,那么改为找到第一条满足将其以及其之前的边均删除后$x$到根不连通的边即可(这与之前的二分实现上一样)

(可以理解为从前往后尽量删去当前边,并通过二分优化此过程)

另外,当前的非树边默认已经被删除,下同

求非树边时,按dfs序从后往前考虑点$x$,一条非树边以$x$为一个端点当且仅当将$x$到父亲的边删除、将这条边加入后$x$到根不连通,同样可以二分实现

找到边后,还需要求出该边另一个端点,也即求dfs序最大的点满足将其和$x$到父亲的边删除、将这条边加入后$x$到根仍连通,同样也可以二分实现

(这里的两个二分同样不具备单调性,将其前缀/后缀均加入/删除即可)

重复此过程也即可得到所有非树边,询问次数的分析与之前类似

最终,总查询次数为$o(mlog 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
原文地址:https://www.cnblogs.com/PYWBKTDA/p/15493965.html