定义$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. Zookeeper的选举机制和同步机制超详细讲解,面试经常问到!

    前言 zookeeper相信大家都不陌生,很多分布式中间件都利用zk来提供分布式一致性协调的特性.dubbo官方推荐使用zk作为注册中心,zk也是hadoop和Hbase的重要组件.其他知名的开源中间 ...

  2. SpringIOC 理论推导

    IOC理论实现 UserDao接口 public interface UserDao { void say(); } UserDaoImpl实现类 public class UserDaoImpl i ...

  3. PTA数据结构 习题3.6 一元多项式的乘法与加法运算 (20分)

    一元多项式的乘法与加法运算 https://pintia.cn/problem-sets/434/problems/5865 设计函数分别求两个一元多项式的乘积与和. 时间限制:200 ms 内存限制 ...

  4. Go语言核心36讲(Go语言进阶技术四)--学习笔记

    10 | 通道的基本操作 作为 Go 语言最有特色的数据类型,通道(channel)完全可以与 goroutine(也可称为 go 程)并驾齐驱,共同代表 Go 语言独有的并发编程模式和编程哲学. D ...

  5. javascript的变量及数据类型

    1.变量的概念 变量是储存数据的内存空间 2.变量的命名规则 js变量的命名规则如下:以字母或者下划线开头可以包含字母.数字.下划线,不能包含特殊字符 3.变量的创建及初始化方法 方法一:先创建后使用 ...

  6. OO第二单元电梯作业总结

    目录 目录一.第一次作业分析设计策略基于度量分析程序结构二.第二次作业分析设计策略基于度量分析程序结构三.第三次作业分析设计策略基于度量分析程序结构四.分析自己程序的bug五.发现别人程序bug所采用 ...

  7. BUAA2020软工作业(一)——谈谈我和计算机的缘分

    项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 第一次作业-热身! 我在这个课程的目标是 进一步提高自己的编码能力,工程能力 这个作业在哪个具体方 ...

  8. 色彩滤镜矩阵(Color Filter Array)

    数码相机上的每个象素都带有一个光感应器,用以测量光线的明亮程度.由于光电二极管是只支持单颜色的装置,它不能区别不同波长的光线.因此,数码相机工程师在相机感应器的上部装上了一套镶嵌式的颜色滤镜,一个颜色 ...

  9. STL模板

    目录 栈stack 队列queue 列表List 集合set 映射map 多重映射multimap 对pair 元组tuple 容器containers 算法algorithms 仿函数/函数对象fu ...

  10. arduino 使用 analogRead 读取不到数据,digitalRead 却可以正常读取

    项目场景: 最近在使用安信可的 ESP32S P14 引脚(ADC 16)读取一个电路状态的时候遇到一个问题,电路状态不是很稳定,在高电平的时候,会突然出现毫秒级的波动,出现短暂的低电平,造成设备状态 ...