【筆記】競賽圖


才知道有這么個神奇的玩意。

定義,\(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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM