CF1710D Recover the Tree

CF1710D Recover the Tree

题意

根据题意构造出一棵合法的树。

nn 个点。ai,j={0,1},ija_{i,j}=\{0,1\},i \le j 表示编号在 [i,j][i,j] 的点是否连通。

n2000n \le 2000

保证有解。

思路

显然是先考虑小区间,然后合并到大区间。

思考只有 a1,n=1a_{1,n}=1 的情况怎么做。

i,i+1i,i+1 不能连边。i,i+k(k>1)i,i+k(k>1) 可以连边。但是不可以有更小的区间连通。

一种构造方案是连 (2,n),(1,i),3in1(2,n),(1,i),3 \le i \le n-1

n=3n=3 时无解。


考虑一般情况。

考虑按顺序一个一个加点。现在加到点 nn

由于树的连通块形式,如果 [l1,r1],[l2,r2][l_1,r_1],[l_2,r_2] 连通。且 l2r1l_2 \le r_1,那么 [l2,r1],[l1,r2][l_2,r_1],[l_1,r_2] 也是连通的。即交和并都是连通的。

那么此时 [1,n1][1,n-1] 已经有了若干个极大的连通区间。

假设 ai,n=1a_{i,n}=1,如何合并连通块。

参考之前的构造,首先连 (i,n)(i,n),如果中间还有多个连通块,然后连 nn 和第二块连通块的头,然后连 ii 和其他连通块的尾。

小到大枚举 nn,从大到小枚举 ii 容易证明是对的。怎么想到的我就不知道了。

时间复杂度 O(n2)O(n^2)

code

好写的。

#include<bits/stdc++.h>
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
using namespace std;
typedef long long ll;
namespace wing_heart {
constexpr int N=2e3+7;
#define gc getchar
typedef pair<int,int> pii;
#define fi first
#define se second
int t;
int n;
char a;
vector<int> vec[N];
bool cmp (int a,int b) { return a>b; }
void main() {
sf("%d",&t);
while(t--) {
sf("%d",&n);
rep(i,1,n) vec[i].clear();
rep(i,1,n) rep(j,i,n) {
a=gc();
while(a!='1' && a!='0') a=gc();
if(a=='1' && j!=i) vec[j].push_back(i);
}
rep(i,1,n) sort(vec[i].begin(),vec[i].end(),cmp);
stack<pii> que;
rep(r,1,n) {
que.push({r,r});
for(int x : vec[r]) {
vector<pii> tmp;
if(que.top().fi<=x) continue;
while(que.size() && que.top().se>=x) tmp.push_back(que.top()), que.pop();
pf("%d %d\n",x,r);
if(tmp.size()>2) pf("%d %d\n",r,tmp[tmp.size()-2].fi);
rep(i,1,(int)tmp.size()-3) pf("%d %d\n",x,tmp[i].se);
que.push({tmp.back().fi,r});
}
}
}
}
}
int main() {
#ifdef LOCAL
freopen("in.txt","r",stdin);
freopen("my.out","w",stdout);
#endif
wing_heart :: main();
}
posted @   wing_heart  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示