CF1710D Recover the Tree 题解
题目描述
\(T\) 组数据,给定二维 \(01\) 数组 s[l][r] ,表示第 \([l,r]\) 个点是否构成一个连通块。
要求构造一棵树满足上述条件,保证有解。
数据范围
- \(1\le T\le 1000,1\le n,\sum n\le 2000\) 。
时间限制 \(\texttt{3s}\) ,空间限制 \(\texttt{256MB}\) 。
分析
神仙构造题。
核心思路:每次合并若干个极长连通块,连通块内部连边可以递归下去做。
极长连通块定义:
s[l][r]=1,且 \(\forall [x,y]\sub [l,r]\) ,有s[x][y]=0。
假如当前正在调用 solve(l,r) ,保证 \([l,r]\) 是极长连通块,我们希望将 [l,r] 构造成一棵树。
将极长连通块缩成一个点,问题转化为:
\(m\) 个点 \(1,2,\cdots,m\) ,满足
s[1][m]=1,其余均为零,构造一棵树符合上述条件。
这个问题的解是平凡的,按照奇偶性连边 \(\cdots-5-3-1-m-2-4-\cdots\) 即可。
代码实现时,通过递归找极长连通块比较麻烦,按照区间长度升序枚举,记录 \(i\) 所属极长连通块 [L[i],R[i]] 即可。
时间复杂度 \(\mathcal O(n^2)\) 。
#include<bits/stdc++.h>
#define fi first
#define se second
#define mp make_pair
#define pii pair<int,int>
using namespace std;
const int maxn=2005;
int n,t;
int L[maxn],R[maxn];
vector<pii> vec;
char s[maxn][maxn];
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d",&n),vec.clear();
for(int i=1;i<=n;i++) scanf("%s",s[i]+i);
for(int i=1;i<=n;i++) L[i]=R[i]=i;
for(int len=1;len<=n;len++)
for(int l=1,r=len;r<=n;l++,r++)
if(s[l][r]=='1'&&L[l]!=L[r]&&R[l]!=R[r])
{
static int lst[2];
lst[0]=l,lst[1]=r,vec.push_back(mp(l,r));
for(int i=R[l]+1,cur=0;i<L[r];i++)
if(L[i]==i)
{
cur^=1,vec.push_back(mp(lst[cur],i));
lst[cur]=i;
}
int nl=L[l],nr=R[r];
for(int i=nl;i<=nr;i++) L[i]=nl,R[i]=nr;
}
for(auto p:vec) printf("%d %d\n",p.fi,p.se);
}
return 0;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/16558245.html
浙公网安备 33010602011771号