竞赛图性质研究(强联通分量/哈密顿回路)
前置知识
竞赛图,强联通分量,哈密顿回路的定义。
定义点 \(i\) 的入读为 \(in_i\)。
强联通分量相关
结论 1
竞赛图强连通缩点后的 \(\texttt{DAG}\) 呈链状,并且前面的所有点向后面的所有点连边,下图中每个圆块为原图的一个强联通分量。
证明:考虑归纳,逐强连通块加入。设目前有一条链,插入一个新连通块 \(x\)。
如果 \(x\) 连向所有点,放在链头;如果所有点连向 \(x\),放在链尾。
否则 \(x\) 的出边一定都在 \(x\) 的入边的后边(否则成环)。
找到分界点,把 \(x\) 插在中间即可。
结论 2
在竞赛图中,如果存在路径 \(u\to v\) 却不存在路径 \(v\to u\),即 \(u\) 所在的强联通分量的拓扑序严格小于 \(v\) 的,则 \(in_u<in_v\)。
证明:设 \(v\) 所在的强联通分量为 \(t\)。考虑缩点后 \(1\to t\) 的“链”,设链内包含的所有点构成集合 \(S\)。
由于不存在路径 \(v\to u\),所以 \(v\notin S\),且由于是竞赛图,于是 \(\forall x\in S\),存在 \(x\to v\) 的有向边,于是 \(in_v\ge |S|\)。
由于 \(S\) 外的点显然与 \(u\) 没有连边,而且 \(u\in S\),于是 \(in_u<|S|\le in_v\)。
结论 3
已知竞赛图所有点的 \(in\),则可以确定每个强联通分量内包含的点。
证明:考虑按某种拓扑序遍历所有强联通分量,设遍历到的前若干个强联通分量里的所有点构成的集合为 \(S\),则 \(\dfrac{|S|(|S|-1)}{2}=\sum\limits_{x\in S} in_x\)。因为按拓扑序,后遍历的点一定不能与先遍历的点连边。
考虑把所有点按 \(in\) 升序排序成序列 \({a}\),由于结论 \(2\) 此时顺序遍历可以类比为拓扑序中顺序遍历。此时若 \(\dfrac{N(N-1)}{2}=\sum\limits_{i=1}^N in_{a_i}\),此时产生了一个新的强联通分量拓扑序遍历前缀。
考虑上一个满足条件的是 \(N'\),初始 \(N'=0\)。则 \(S=\{a_x:x\in(N',N]\}\) 构成了一个新的强联通分量。由于拓扑序这样显然是不重不漏的。
哈密顿回路/路径相关
结论1
任意竞赛图存在一条哈密顿路径。
结论2
任意强联通的竞赛图存在哈密顿回路,考虑构造性证明。
归纳,首先如上构造出一条哈密顿路径。由于强联通性,此时能找到一个点连向起点,于是构造出一个小哈密顿回路,考虑逐步扩展。
首先如果又往后找到了一个连向起点 \(S\) 的点,直接扩展过去即可,如下图:
否则考虑 \(S\to T\) 路径上第一个点 \(x\),使得当前 \(T\) 往后存在一个点连向 \(x\)。记 \(x\) 前面那个点为 \(pre_x\),由于 \(x\) 是第一,于是下图中边 \(pre_x\to nex_T\) 的边存在。
于是构造了 \(x\to T\to S\to pre_x\to nex_T\to i\to x\) 的新哈密顿回路。注意此时 \(x\) 为起点,\(i\) 为终点。由于强联通性能一直构造下去直到包含所有点。
例题
-
CF1779E。注意到能留下的点等价于这个点能游走经过所有点,等价与这个点在竞赛图缩点的链头强联通分量中。
要按上述方法找出这个强联通分量,只需确定所有点的 \(in\),每次询问除了这个点的其他所有点,得到这个点的 \(out\),则 \(in_i=n-1-out_i\),\(\texttt{record}\)。 -
CF1498E。这题不需要询问。发现给定了竞赛图每个点的 \(in\),于是确定了所有强联通分量,每次求 \(\max-\min\) 取个最大值就知道题目所求的极差了。
-
P3561。首先按上文结论 \(3\) 直接找出所有强联通块,然后按上文对每个强联通块找出哈密顿回路,由于缩点后的形状之后随便做即可。有点难写。
$3$ 的 $\texttt{code}$
#include<bits/stdc++.h>
#define LL long long
#define bs basic_string<int>
#define fr(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);
using namespace std;
const int N=2e3+5;
int n,m,b[N],p[N],in[N],w[N],to[N],mx[N];bool a[N][N];
bs ans[N],rg[N],g,h,H;
inline void add(int u,int v){a[u][v]=1;in[v]++;}
inline void upd(bs &f,int x,int y)
{
bs g;for(int i=0;i<=x;i++) g+=f[i];g+=y;
for(int i=x+1;i<f.size();i++) g+=f[i];f=g;
}
inline void check()
{
for(int i=1;i<=n;i++)
{
int len=ans[i].size();
for(int j=0;j<len-1;j++)
{
int k=ans[i][j],l=ans[i][(j+1)%len];
if(!a[k][l]){cout<<"No\n"<<i<<" "<<k<<" "<<l;exit(0);}
}
}cout<<"Yes\n";
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin>>n;
for(int i=2;i<=n;i++) for(int j=1,o;j<i;j++) cin>>o,o?add(j,i):add(i,j);
iota(p+1,p+1+n,1);sort(p+1,p+1+n,[](int x,int y){return in[x]<in[y];});
for(int i=1,s=0,t;i<=n;i++)
{
t=p[i],s+=in[t];
if(s==i*(i-1)/2) b[++m]=i;
}
for(int i=1;i<=m;i++)
{
int l=b[i-1]+1,r=b[i];g.clear();
if(l==r){rg[i]+=p[l];continue;}g+=p[l];g+=p[l+1];
if(!a[p[l]][p[l+1]]) swap(g[0],g[1]);
for(int j=l+2;j<=r;j++)
{
int w=p[j],s=g[0],t=g.back();
if(a[w][s]) upd(g,-1,w);
else if(a[t][w]) g.push_back(w);
else
{
int W=-1;
for(int k=0;k<g.size();k++) if(a[g[k]][w]) W=k;
upd(g,W,w);
}
}
memset(to,-1,sizeof(to));memset(mx,-1,sizeof(mx));
for(int j=0;j<g.size();j++) to[g[j]]=j;
for(int j:g) for(int k=1;k<=n;k++) if(a[k][j]) mx[j]=max(mx[j],to[k]);
h.clear();int w=0;
for(int j=0;j<g.size();j++)
{
h+=g[j];
if(a[g[j]][g[0]]){w=j;break;}
}
while(w!=g.size()-1)
{
int W=0;
for(int j=0;j<h.size();j++) if(mx[h[j]]>w){W=j;break;}
int r=mx[h[W]];H.clear();
for(int j=W;j<h.size();j++) H+=h[j];
for(int j=0;j<W;j++) H+=h[j];
for(int j=w+1;j<=r;j++) H+=g[j];w=r;h=H;
}
rg[i]=h;
}
for(int i=1;i<=m;i++)
{
int l=b[i-1]+1,r=b[i],len=r-l+1;g=rg[i];
for(int j=0;j<len;j++) w[g[j]]=j;
for(int j=l;j<=r;j++)
{
int t=p[j],w=::w[t];
for(int k=0;k<len;k++,w++,(w==len)&&(w=0)) ans[t]+=g[w];
for(int k=i+1;k<=m;k++) for(int l:rg[k]) ans[t]+=l;
}
}
// check();
for(int i=1;i<=n;i++){cout<<ans[i].size()<<" ";for(int j:ans[i]) cout<<j<<" ";cout<<"\n";}
return 0;
}
一种特殊竞赛图的性质
例题:CF1338E。请自行阅读题解。