peiwenjun's blog 没有知识的荒原

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;
}

posted on 2022-08-06 23:50  peiwenjun  阅读(8)  评论(0)    收藏  举报

导航