Loading

【笔记】竞赛图

才知道有这么个神奇的玩意。

定义,\(n\) 个点,任意两点之间存在且恰好存在一条有向边的图成为 \(n\) 阶竞赛图。

性质 \(1\) :一定存在一条哈密顿路径。

证明:数学归纳法,\(n=1\) 显然成立,当 \(n-1\) 成立时的哈密顿路径,存在相邻两点\(v_i,v_{i+1}\),使得 \(v_i\to n\ \land\ n\to v_{i+1}\) 则可以当第 \(n\) 个点插入。否则第 \(n\) 个点一定可以插入路径首/尾。

性质 \(2\) :缩点后一定是一条链。

直接证明:因为存在一条哈密顿路径,所以缩点一定是路径上连续的一段,因此缩完之后还是一条链。

反证法:假设不存在一条链,而整张图又弱连通,所以缩完点后存在 \(x\to z,y\to z\)。由于是竞赛图,\(x,y\)之间一定存在有向边,所以\(x,y\)可以重新缩点或者形成一条链。

性质 \(3\) :竞赛图的每一个强连通都存在哈密顿环。

证明:数学归纳法。\(n=1\) 显然成立,当 \(n-1\) 成立时,对于第 \(n\) 个点,如果单独形成 \(\rm SCC\) 显然成立。否则如果加入另一个\(\rm SCC\),则存在 \(x\to n,n\to y\),其中\(x,y\)属于该\(\rm SCC\) 且在环中相邻,那么可以在 \(x,y\) 之间加入第 \(n\) 个点形成新的哈密顿环。

为什么 \(x,y\) 一定在原环中相邻呢?如果不相邻,则原来的环上每一点都指向 \(n\) ,或都被 \(n\) 指向,不能形成新的 \(\rm SCC\),与假设矛盾。

性质 \(4\) :如果 \(x\) 的出度大于 \(y\) 的出度,则 \(x\) 可以到达 \(y\)

如果 \(x\)\(y\) 在同一个强连通分量,则显然成立。

否则我们缩点后拓扑排序,第 \(x\) 所在 \(\rm SCC\) 标号 \(u\)\(y\) 所在 \(\rm SCC\) 标号 \(v\)

如果 \(u>v\) ,则 \(x\) 向 $v $ 中所有点连边,而 \(y\) 只能向标号 \(>v\) 的点连边。同时 \(x\) 也向所有标号 \(>v\) 的点连边,所以 \(x\) 的度数一定大于 \(y\)

因此,如果\(v<u\),则 \(x\) 的度数小于 \(y\) 与条件不符,所以 \(x\) 的出度大于 \(y\) 的出度是 \(x\) 可以到达 \(y\) 的充分条件。

CF1498E Two Houses

比如这道题的交互方式已经告诉了我们解法。

在我们得到第一个\(\texttt{Yes}\)后需要结束询问,也就意味着我们需要构造询问使得得到的\(\texttt{Yes}\)能告诉我们两点是强连通的。

题目还给定了入度\(k_i\),我们可以猜测,如果入度大的点能够到达入度小的点,则两个点强连通。

证明:

我们对竞赛图使用强连通分量缩点,然后跑拓扑排序。则在拓扑序中相邻的两个分量,第一个分量中的任意一个点必定向第二个分量中所有点连边,则出度\(\ge sz_r\),而第二个分量不能向第一个分量连边,因为如果右边则可以继续缩点,所以出度\(<sz_r\)。对于其余的联通分量,由于是完全图,则与两个联通分量中连有相同方向的边。

所以如果两个点\(k_i<k_j\),则 \(i\) 的出度大于 \(j\) 的出度。如果 \(i,j\) 在一个联通分量,则显然强连通。否则 \(i\) 一定能到达 \(j\),如果 \(j\) 又能到达 \(i\),则两个点强连通。

一个没有太大用的性质,就是对于 \(k=0\) 的点 ,只有出边,可以将这个点删掉,并将其他 \(k\)\(1\)。这样可以大大减少交互次数。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define N 505
using namespace std;
int n,T;
typedef pair<int,int> Pr;
Pr k[N];
struct node{
	int l,r,val;
	bool operator<(const node o)const{
		return val>o.val;
	}
}a[N*N];
int main(){
	cin>>n;
	rep(i,1,n)cin>>k[i].first,k[i].second=i;
	sort(k+1,k+n+1);
	int j=1;
	while(j<=n&&k[j].first==0){
		rep(u,j+1,n)k[u].first--;
		j++;
	}
	rep(x,j,n)rep(y,x+1,n)a[++T].l=k[x].second,a[T].r=k[y].second,a[T].val=k[y].first-k[x].first;
	sort(a+1,a+T+1);
	rep(i,1,T){
		cout<<"? "<<a[i].r<<" "<<a[i].l<<endl;
		string op;cin>>op;
		if(op=="Yes"){cout<<"! "<<a[i].l<<" "<<a[i].r<<endl;return 0;}
	}
	cout<<"! 0 0"<<endl;
	return 0;
} 

CF1514E

竞赛图一定存在一条哈密顿路径。

我们先找到哈密顿路径,然后缩点的时候一定是连续的一段缩成一个点。

考虑寻找哈密顿路径,我们递归处理。先找前 \(i-1\) 个点的哈密顿路径,再将第 \(i\) 个点插入。这是个经典问题直接二分即可。

至于缩点,我们倒叙枚举当前点 \(i\) ,找到 \(i\sim n\) 的点中能到达的哈密顿路径中最前面的点的标号。

不难发现能到达的最前面的点具有单调性,直接双指针扫一遍即可。

注意每组测试数据结束后一定要再读入一个反馈(因为这个调了一个小时

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 105
using namespace std;
int n,u[N],fa[N],mat[N];
bool get(int x,int y){
	cout<<"1 "<<x-1<<" "<<y-1<<endl;
	int op;cin>>op;return op;
}
bool ask(int x,int l,int r){
	cout<<"2 "<<u[x]-1<<" "<<r-l+1<<" ";
	rep(i,l,r)cout<<u[i]-1<<" ";
	cout<<endl;int op;cin>>op;return op;
}
void solve(){
	memset(fa,0,sizeof(fa));
	memset(u,0,sizeof(u));
	memset(mat,0,sizeof(mat));
	cin>>n;
	u[1]=1;fa[1]=1;
	int tot=0;
	rep(i,2,n){
		int l=0,r=i;
		while(l+1<r){
			int mid=(l+r)>>1;
			int cur=get(u[mid],i);
			tot++;
			if(cur)l=mid;else r=mid;
		}
		pre(j,i-1,r)u[j+1]=u[j];
		u[l+1]=i;
	}
	int cur=n;
	pre(i,n,2){
		cur=min(cur,i);
		while(cur>1&&ask(i,1,cur-1))cur--;
		fa[i]=cur;
	}
	rep(i,1,n)rep(j,fa[i],i)fa[i]=min(fa[i],fa[j]);
	rep(i,1,n)mat[u[i]]=i;
	cout<<"3 "<<endl;
	rep(i,1,n){
		rep(j,1,n)if(fa[mat[i]]==fa[mat[j]])putchar('1');
		else if(mat[i]<mat[j])putchar('1');else putchar('0');
		cout<<endl;
	}int op;cin>>op;
}
int main(){
	int T;scanf("%d",&T);
	while(T--)solve();
	return 0;
}
posted @ 2021-12-16 17:18  7KByte  阅读(1018)  评论(0编辑  收藏  举报