1 条题解

  • 0
    @ 2025-8-24 22:46:11

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar FFTotoro
    龙猫

    搬运于2025-08-24 22:46:11,当前版本为作者最后更新于2023-04-04 20:53:08,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    前言

    赛时只打出了特殊性质,还是太菜了 /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

    放代码:

    /*
    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;
    }
    
    • 1

    信息

    ID
    8565
    时间
    1000ms
    内存
    128MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者