专栏文章
[USACO23OPEN] Tree Merging G 题解
P9191题解参与者 7已保存评论 7
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 7 条
- 当前快照
- 1 份
- 快照标识符
- @mkmfwx2h
- 此快照首次捕获于
- 2026/01/20 18:17 4 周前
- 此快照最后确认于
- 2026/01/20 18:17 4 周前
前言
赛时只打出了特殊性质,还是太菜了 /kk。
解法
以下描述中,“原始状态”指的是未进行任何合并操作的树,“目标状态”指的是“进行完所有合并操作后的树”, 表示节点 在“原始状态”中的子节点集合, 表示节点 在“目标状态”中的子节点集合, 表示在目标状态中不存在的节点在操作过程中和它进行合并的节点。
令 为“节点 是否可以合并进节点 ”。显然的,若 为真,那么 和 必须满足以下几个条件:
-
目标状态包含节点 ;
-
,等号能取到当且仅当目标状态包含节点 ;
-
, 使得 为真。
计算出 不是难事,可以根据定义,从树的叶子节点开始按照深度递减一直算到根节点即可。
最后构造方案时,从根节点开始按照深度递增一直算到叶子节点,枚举每一个深度指定的节点 ,令其在原状态中的父亲为 ,那么只要找到一个最大的节点 满足 在目标状态中的父亲与 相等(这样可以保证 现在有同一个父亲)且 为真,若最终 就合并 。
放代码:
CPP/*
ID: CrowMatrix
TASK: Tree Merging
LANG: C++
*/
#include<bits/stdc++.h>
using namespace std;
int p1[1001],p2[1001],d[1001],w[1001];
bool e[1001],c[1001][1001];
int main(){
ios::sync_with_stdio(false);
int t; cin>>t;
while(t--){
int n,r; cin>>n;
for(int i=1;i<=n;i++)p1[i]=p2[i]=e[i]=w[i]=d[i]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)c[i][j]=false;
for(int i=1;i<n;i++){
int v,p; cin>>v>>p; p1[v]=p;
} // 原状态
for(int i=1;i<=n;i++)if(!p1[i])r=i;
int m; cin>>m; e[r]=true;
for(int i=1;i<m;i++){
int v,p; cin>>v>>p; p2[v]=p,e[v]=true;
} // 目标状态
for(int i=n;i;i--)
for(int j=1;j<=n;j++)
if(j!=r)d[j]=d[p1[j]]+1; // 计算节点深度
for(int i=n;i;i--)
for(int j=1;j<=n;j++)
if(d[j]==i)
if(e[j])c[j][j]=true;
else for(int k=j;k<=n;k++)
if(e[k])for(int l=c[j][k]=1;l<=n;l++)
if(p1[l]==j){
bool f=false;
for(int p=1;p<=n;p++)
f|=p2[p]==k&&c[l][p];
c[j][k]&=f; // 逐一检查各项条件是否满足
}
cout<<n-m<<endl; w[r]=r;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(d[j]==i){
for(int k=1;k<=n;k++)
if(p2[k]==w[p1[j]]&&c[j][k])w[j]=k; // 找合并的目标
if(j!=w[j])cout<<j<<' '<<w[j]<<endl;
}
}
return 0;
}
相关推荐
评论
共 7 条评论,欢迎与作者交流。
正在加载评论...