专栏文章

[USACO23OPEN] Tree Merging G 题解

P9191题解参与者 7已保存评论 7

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
7 条
当前快照
1 份
快照标识符
@mkmfwx2h
此快照首次捕获于
2026/01/20 18:17
4 周前
此快照最后确认于
2026/01/20 18:17
4 周前
查看原文

前言

赛时只打出了特殊性质,还是太菜了 /kk。

解法

以下描述中,“原始状态”指的是未进行任何合并操作的树,“目标状态”指的是“进行完所有合并操作后的树”,K1(a)K_1(a) 表示节点 aa 在“原始状态”中的子节点集合,K2(a)K_2(a) 表示节点 aa 在“目标状态”中的子节点集合,waw_a 表示在目标状态中不存在的节点在操作过程中和它进行合并的节点。
ca,bc_{a,b} 为“节点 aa 是否可以合并进节点 bb”。显然的,若 ca,bc_{a,b} 为真,那么 aabb 必须满足以下几个条件:
  • 目标状态包含节点 bb
  • aba\le b,等号能取到当且仅当目标状态包含节点 aa
  • iK1(a)\forall i\in K_1(a)jK2(b)\exists j\in K_2(b) 使得 ci,jc_{i,j} 为真。
计算出 ca,bc_{a,b} 不是难事,可以根据定义,从树的叶子节点开始按照深度递减一直算到根节点即可。
最后构造方案时,从根节点开始按照深度递增一直算到叶子节点,枚举每一个深度指定的节点 aa,令其在原状态中的父亲为 ff,那么只要找到一个最大的节点 bb 满足 bb 在目标状态中的父亲与 wfw_f 相等(这样可以保证 a,ba,b 现在有同一个父亲)且 ca,bc_{a,b} 为真,若最终 aba\ne b 就合并 a,ba,b
放代码:
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 条评论,欢迎与作者交流。

正在加载评论...