定义$f(S)$表示点集$S$的最小连通块


做法1

通过对所有节点判定,可以在$n$次询问中求出具体的$f(S)$

对于$x\ne y$,显然$(x,y)\in E$当且仅当$f(\{x,y\})=\{x,y\}$,那么直接暴力判定即可

询问次数和时间复杂度均为$o(n^{3})$


做法2

为了方便,以下认为原树为有根树,且以1为根

对于$x\ne y$,显然$x$为$y$的祖先当且仅当$x\in f(\{1,y\})$,同样直接暴力判定即可

得到任意两点祖先后代关系后,再拓扑排序即可得到整棵树

询问次数和时间复杂度均为$o(n^{2})$


做法3

将上述结论拓展,即对于$x\not\in S$,$x\in f(\{1\}\cup S)$当且仅当$S$中存在$x$的后代

若满足此条件,则可以进行以下两种操作——

在$S$中二分,可以在$o(\log |S|)$次询问和$o(|S|)$的复杂度内找到某个$S$中$x$的后代

在$S$中搜索,即重复上述二分并在当前$S$中不存在$x$的后代时结束,可以在$o(m\log |S|)$次询问和$o(m|S|\})$的复杂度内找到$S$中所有$x$的后代(其中$m$为$S$中$x$的后代数)

下面,如果将原树看作有向树(方向为儿子到父亲),并得到了其任意一个拓扑序,那么即可利用上述做法得到整棵树,具体做法如下:

维护当前点集$S$(初始为$V$),并从前往后枚举拓扑序上的点$x$,并在$S$中找到$x$的儿子并删除,不难发现此时$S$中所有$x$的后代必然都是$x$的儿子,因此通过前面的搜索来实现

注意到每一个点恰被计入$m$一次,因此询问次数约为$o(n\log n)$,时间复杂度为$o(n^{2})$

显然这个复杂度是可以接受的,那么以下问题即变为如何求拓扑序


做法4

考虑拓扑序,实际上即不断找到原树的一个叶子,而$x$为叶子当且仅当$x\not\in f(V-\{x\})$

由此,可以在$n$次询问和$o(n^{2})$的复杂度内找到一个叶子,重复此操作即可(注意$V$要删除得到的叶子)

询问次数为$o(n^{2})$,时间复杂度为$o(n^{3})$


做法5

根据上述分析,实际上是需要优化找叶子的复杂度

考虑这样一个构造:从根节点开始,不断随机找到一个后代并变为该后代

记当前节点为$x$,选出$x$子树中所有子树大小$>\frac{sz_{x}}{2}$的点,假设有$s$个(不包括$x$),那么这些节点中最深的一个子树中至多有$sz_{x}-s$个点,也即得到$s<\frac{sz_{x}}{2}$,由于$s$为整数也可以看作$s\le \frac{sz_{x}-1}{2}$

换言之,每一次递归有$\frac{1}{2}$的概率子树大小除以2,那么期望两步使得子树大小除以2,因此子树大小从$n$变为1(叶子)的期望步数即$2\log n$(实际可能略少)

关于如何找到某个后代,(若$x$不为叶子)$V-\{x\}$中总存在$x$的后代,再使用之前的二分即可

询问次数为$o(n\log^{2}n)$,时间复杂度为$o(n^{2}\log n)$


做法6

换一个思路,去减少找叶子的轮数,即每一次取出所有叶子一起处理

但这样显然会被一条链卡掉,那么再对于每一个叶子向上找一条链,满足链上的点(目前)都仅有一个儿子(注意节点会被删除),将这条链上的点也删除

令$T(x)$为$x$被删除时的轮数(初始叶子有$T(x)=1$),根据上述过程,当$T(son)$中最大值不唯一时$T(x)$即为$T(son)$的最大值,否则$T(x)$为$T(son)$最大值+1

归纳证明$T(x)\ge n$的$x$满足$sz_{x}\ge 2^{n}-1$,根据转移显然成立,因此轮数即为$\log n$

下面,问题即如何找到这样的链:

首先,仍然按照做法4,找出所有叶子,假设其构成集合$L$

一个点在链上,当且仅当其恰有一个后代在$L$中,这可以先二分找到$L$中的某个后代,再判定剩下的部分中是否还存在后代,进而每一个后代对应的点即构成其向上的链

为了保证这些点的拓扑序,再将其sort一下即可

询问次数为$o(n\log^{2}n)$,时间复杂度为$o(n^{2}\log n)$


做法7

不再去找叶子,而是考虑分治——

构造函数$solve(S,x)$,保证$S$是$x$子树中若干个点(包含$x$),其需要求出$S$的一个拓扑序

若$S=\{x\}$显然拓扑序即仅含$x$,否则考虑分治:任取$S-\{x\}$中的一个元素$y$,并找出$S$中是$y$后代的元素,然后将该部分从$S$中取出分别求拓扑序

对$S-\{y\}$中的点染色,两点同色当且仅当删除$y$后两点在同一个连通块中,并称$x$的颜色为"白色",其余颜色为"彩色",显然判定$S'\subseteq S$中的点是否同色即等价于$y\not\in f(S')$

进一步的,将$S$中的点排在序列上,并利用上述操作不断找到下一个极长连续同色段,那么即有段数次二分

(注意只需要区分白色和彩色,而不需要区分具体的颜色)

假设颜色相同的点最多有$m$个,注意到剩下的点只有$|S|-m$个,因此不难发现至多只有$o(|S|-m)$段

换言之,可以看作每一次不属于这$m$个点的点都有一次二分的贡献

考虑每一个点的贡献,对该点颜色分类讨论:

1.为白色点,注意到该次分治$S$必然缩小一半,至多$\log n$次

2.为彩色点,再分类讨论:

(1)这$m$个点为白色,同样$S$必然缩小一半,至多$\log n$次

(2)这$m$个点为彩色(且不为该点的颜色),那么其必然是$y$的轻儿子,而每一个$y$只被以此法访问一次且每一个点到根路径上只有$\log n$条轻边,因此同样至多$\log n$次

综上,至多$o(n\log n)$次二分

询问次数为$o(n\log^{2}n)$,时间复杂度为$o(n^{2}\log n)$


做法8

实际上,并不需要真的划分出$y$的子树,不妨直接递归

具体的,重新构造函数$solve(S,x)$,其需要求出$S$与$x$子树交的一个拓扑序(保证$x\in S$)

若$S-\{x\}$中不存在$x$的后代,那么该拓扑序即仅包含$x$,直接返回即可

否则,二分找到$S-\{x\}$中$x$的一个后代$y$,然后执行$solve(S,y)$得到拓扑序,再将该拓扑序中的点(即$S$与$y$子树交的点)在$S$中删除,重复此过程即可

由此执行$solve(V,1)$即可,注意到每一个节点至多执行一次二分,因此共计即$n$次二分

询问次数为$o(n\log n)$,时间复杂度为$o(n^{2})$

 1 #include<bits/stdc++.h>
2 #include"C.hpp"
3 using namespace std;
4 #define N 1005
5 #define pii pair<int,int>
6 #define vi vector<int>
7 int n;
8 vi V,S0,Pos;
9 vector<pii>E;
10 void del(vi &v,int x){
11 for(int i=0;i<v.size();i++)
12 if (v[i]==x){
13 v.erase(v.begin()+i);
14 break;
15 }
16 }
17 int get_one(vi S,int x){
18 del(S,1);
19 if (x==1){
20 if (S.size())return S[0];
21 return 0;
22 }
23 S0=S,S0.push_back(1);
24 if (!ask(S0,x))return 0;
25 int l=0,r=S.size()-1;
26 while (l<r){
27 int mid=(l+r>>1);
28 S0.clear(),S0.push_back(1);
29 for(int i=l;i<=mid;i++)S0.push_back(S[i]);
30 if (ask(S0,x))r=mid;
31 else l=mid+1;
32 }
33 return S[l];
34 }
35 vi get_all(vi S,int x){
36 Pos.clear();
37 while (1){
38 int pos=get_one(S,x);
39 if (!pos)break;
40 Pos.push_back(pos);
41 del(S,pos);
42 }
43 return Pos;
44 }
45 vi calc(vi S,int x){
46 vi ans(0);
47 while (1){
48 del(S,x);
49 int y=get_one(S,x);
50 if (!y){
51 ans.push_back(x);
52 break;
53 }
54 vi s=calc(S,y);
55 for(int i=0;i<s.size();i++){
56 ans.push_back(s[i]);
57 del(S,s[i]);
58 }
59 }
60 return ans;
61 }
62 vector<pii> get_E(vi order){
63 V.clear(),E.clear();
64 for(int i=1;i<=n;i++)V.push_back(i);
65 for(int i=0;i<n;i++){
66 del(V,order[i]);
67 vi son=get_all(V,order[i]);
68 V.push_back(order[i]);
69 for(int j=0;j<son.size();j++){
70 E.push_back(make_pair(order[i],son[j]));
71 del(V,son[j]);
72 }
73 }
74 return E;
75 }
76 vector<pii> work(int nn){
77 n=nn;
78 for(int i=1;i<=n;i++)V.push_back(i);
79 return get_E(calc(V,1));
80 }

做法9

再换一个思路,直接从拓扑序上考虑

具体的,拓扑序$\{a_{i}\}$的限制即不存在$1\le x<y\le n$,使得$a_{x}$是$a_{y}$的祖先

注意到这类似于逆序对,即联想到排序,那么初始令$a_{i}=i$,并执行插入排序

换言之,即需要对于已有的拓扑序$\{a_{1},a_{2},...,a_{k}\}$,加入一个新的$x$并保证拓扑序的性质

根据之前的性质,找到最长的后缀使得其中没有$x$的后代,注意到若$x$不为第一个元素,那么$x$的上一个元素必然恰好是$x$的后代,因此更之前的元素中不存在$x$的祖先(否则与其本来为拓扑序矛盾),因此所有与$x$相关的点对仍满足上述性质,即仍为拓扑序

显然该过程可以二分,同样也仅包含$n$次二分

询问次数为$o(n\log n)$,时间复杂度为$o(n^{2})$

 1 #include<bits/stdc++.h>
2 #include"C.hpp"
3 using namespace std;
4 #define N 1005
5 #define pii pair<int,int>
6 #define vi vector<int>
7 int n;
8 vi V,S0,Pos,order;
9 vector<pii>E;
10 void del(vi &v,int x){
11 for(int i=0;i<v.size();i++)
12 if (v[i]==x){
13 v.erase(v.begin()+i);
14 break;
15 }
16 }
17 int get_one(vi S,int x){
18 S0=S,S0.push_back(1);
19 if (!ask(S0,x))return -1;
20 int l=0,r=S.size()-1;
21 while (l<r){
22 int mid=(l+r>>1);
23 S0.clear(),S0.push_back(1);
24 for(int i=mid+1;i<=r;i++)S0.push_back(S[i]);
25 if (ask(S0,x))l=mid+1;
26 else r=mid;
27 }
28 return l;
29 }
30 vi get_all(vi S,int x){
31 Pos.clear();
32 del(S,1);
33 if (x==1)return S;
34 while (1){
35 int pos=get_one(S,x);
36 if (pos<0)break;
37 Pos.push_back(S[pos]);
38 del(S,S[pos]);
39 }
40 return Pos;
41 }
42 vector<pii> get_E(vi order){
43 V.clear(),E.clear();
44 for(int i=1;i<=n;i++)V.push_back(i);
45 for(int i=0;i<n;i++){
46 del(V,order[i]);
47 vi son=get_all(V,order[i]);
48 V.push_back(order[i]);
49 for(int j=0;j<son.size();j++){
50 E.push_back(make_pair(order[i],son[j]));
51 del(V,son[j]);
52 }
53 }
54 return E;
55 }
56 vector<pii> work(int nn){
57 n=nn;
58 for(int i=2;i<=n;i++){
59 int pos=get_one(order,i)+1;
60 order.insert(order.begin()+pos,i);
61 }
62 order.push_back(1);
63 return get_E(order);
64 }

[loj6736]最小连通块的更多相关文章

  1. ZOJ 3781 Paint the Grid Reloaded(DFS连通块缩点+BFS求最短路)

    题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5268 题目大意:字符一样并且相邻的即为连通.每次可翻转一个连通块X( ...

  2. ZOJ 3781 Paint the Grid Reloaded 连通块

    LINK:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3781 题意:n*m只由OX组成的矩阵,可以选择某一连通块变成另一 ...

  3. tyvj2054 四叶草魔杖——连通块 & 状压DP

    题目:http://www.joyoi.cn/problem/tyvj-2054 把点分成几个连通块,和为0的几个点放在一块,在块内跑最小生成树作为这个块的代价: 然后状压DP,组成全集的最小代价就是 ...

  4. Codeforces 987 K预处理BFS 3n,7n+1随机结论题/不动点逆序对 X&Y=0连边DFS求连通块数目

    A /*Huyyt*/ #include<bits/stdc++.h> #define mem(a,b) memset(a,b,sizeof(a)) #define pb push_bac ...

  5. 洛谷P1991 无线通讯网(最小生成树性质+连通块)

    题目描述 国防部计划用无线网络连接若干个边防哨所.2 种不同的通讯技术用来搭建无线网络: 每个边防哨所都要配备无线电收发器:有一些哨所还可以增配卫星电话. 任意两个配备了一条卫星电话线路的哨所(两边都 ...

  6. DFS序+线段树 hihoCoder 1381 Little Y's Tree(树的连通块的直径和)

    题目链接 #1381 : Little Y's Tree 时间限制:24000ms 单点时限:4000ms 内存限制:512MB 描述 小Y有一棵n个节点的树,每条边都有正的边权. 小J有q个询问,每 ...

  7. UVA 572 油田连通块-并查集解决

    题意:8个方向如果能够连成一块就算是一个连通块,求一共有几个连通块. 分析:网上的题解一般都是dfs,但是今天发现并查集也可以解决,为了方便我自己理解大神的模板,便尝试解这道题目,没想到过了... # ...

  8. HD1269迷宫城堡(有向图 && 划分连通块)

    迷宫城堡 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submis ...

  9. DFS(连通块) ZOJ 2743 Bubble Shooter

    题目传送门 题意:从炮台射出一个球,三个及以上颜色相同的会掉落,问最后会掉落多少个球 分析:先从炮台找一个连通块,然后与顶部连接的连通块都不会掉落,剩下的就是炮台射出后跟随掉落的. #include ...

随机推荐

  1. linux启动redis命令

    首先进入到/usr/local/bin目录下(因为你redis安装的目录绝大多数都在这里) root@xxxx:/usr/local/bin#:redis-server wangconfig/redi ...

  2. bzoj3262陌上花开 (CDQ,BIT)

    题目大意 给定n朵花,每个花有三个属性,定义\(f[i]\)为满足\(a_j \le a_i\)且\(b_j \le b_i\)且\(c_j \le c_i\)的j的数量, 求\(d \in [0,n ...

  3. Java爬虫系列四:使用selenium-java爬取js异步请求的数据

    在之前的系列文章中介绍了如何使用httpclient抓取页面html以及如何用jsoup分析html源文件内容得到我们想要的数据,但是有时候通过这两种方式不能正常抓取到我们想要的数据,比如看如下例子. ...

  4. Android QMUI实战:沉浸式/适配状态栏

    近期研究QMUI换肤的实现,顺便分析了下QMUI的沉浸式. 网上已有很多关于QMUI实现页面沉浸式的文章,简而言之:复杂了. 本期,我们仅通过几行代码,即可完美实现页面沉浸式效果,并轻松匹配换肤的色彩 ...

  5. CAD网页Web端显示开发为什么要以WebGIS的思路来开发?

    背景 在之前的博文CAD图DWG解析WebGIS可视化技术分析总结中讲解了如何把CAD的DWG格式的图纸Web可视化的方案.博文发布后,受到不少同行们的关注,也有不少咨询一些专业问题,其中大家可能疑惑 ...

  6. 深度剖析Redis6的持久化机制(大量图片说明,简洁易懂)

    Redis的强劲性能很大程度上是由于它所有的数据都存储在内存中,当然如果redis重启或者服务器故障导致redis重启,所有存储在内存中的数据就会丢失.但是在某些情况下,我们希望Redis在重启后能够 ...

  7. django通过管理页上传图片

    1.配置目录 新建上传录.static/medis 2.设置上传文件保存路径 # setting.py中设置上传文件路径static/media MEDIA_ROOT = os.path.join(B ...

  8. Java:并发笔记-05

    Java:并发笔记-05 说明:这是看了 bilibili 上 黑马程序员 的课程 java并发编程 后做的笔记 4. 共享模型之内存 本章内容 上一章讲解的 Monitor 主要关注的是访问共享变量 ...

  9. elasticsearch入门(简单的crud操作)

    记录一下,elasticsearch从创建索引到插入数据的一个crud操作. 一.创建索引 curl -XPUT "http://192.168.99.1:9200/productindex ...

  10. Noip模拟73 2021.10.10

    老妈送来了防寒补给就很棒,再也不用晚上盖两层毛巾被了,再也不用担心晚上自动把毛巾被$split$了 还有一些好吃的耶叶 T1 小L的疑惑 考场上疑惑的切掉了 直接把$a$排序然后处理前缀和的过程中判断 ...