zoukankan      html  css  js  c++  java
  • CSP2019 树上的树 口胡

    拖了一年, 今天上午终于把这道题做出来了。


    基本思路

    题目要求字典序最小, 而字典序是有着天然的贪心性质的, 可以比较自然地想到要应用贪心算法, 进一步地, 要思考 “某个数字最终停留在某个点上” 会对全局的删边顺序产生哪些限制。
    比如有这样一条路径: (s--^a--o--^b--o--^c--o--^d--t), 如果初始时点 s 上的数字最终停留在点 t, 那么一定满足:

    1. 边 a 是与点 s 相邻的边中第一个被删去的边
    2. 对于路径上的点 o(们), 在其相邻的边中, b 一定紧接着 a 被删去, c 一定紧接着 b 被删去
    3. 边 d 是与点 t 相邻的边中最后一个被删去的边

    以上任意一条的任意部分不被满足, 初始时点 s 上的数字都不会最终停留在点 t; 以上所有条件都满足, 初始时点 s 上的数字最终就会停留在点 t。
    发现以上的限制都是对于 "与某个点相邻的边" 之间的限制, 自然地认为要维护这个来辅助贪心算法的判断。


    进一步的思路

    有了基本的思路, 就可以思考出算法大致的框架了。
    首先从小到大枚举数字, 找出在满足前面数字形成的限制下其可以到达的标号最小的节点, 然后把限制加上。
    最朴素的找最小节点的思路就是枚举节点, 优化这个朴素思路的方法建立在如下事实上:

    如果以当前数字的初始节点为根, 那么对于任意非根节点, 其与其父亲当作当前数字的最终节点所产生的限制是高度相似的

    那么就可以通过 dfs 来查找当前枚举到的数字能够停留的标号最小的节点。


    最终思路

    仅剩的问题是如何维护与一个点相邻的边之间的相对顺序。
    首先最终这些边的顺序一定是一个序列。
    对于 “让这条边是这个点相邻边中 第一个/最后一个 被删除的边” 这种限制, 可以加哨兵, 这样就把所有的限制都转化成 “一个边要紧接着另一个边之后删” 了。


    代码以及注释

    (时间有限, 对于代码仅做了一些最基本的注释 仅供观赏

    #include<bits/stdc++.h>
    using namespace std;
    
    const int N = 2003;
    
    int n, fa[N], p[N], deg[N];
    
    int ct, hd[N], nt[N<<1], vr[N<<1];
        void ad(int x,int y) {nt[++ct]=hd[x], hd[x]=ct, vr[ct]=y;  }
    
    int f[N][N], t[N][N], siz[N][N]; // 用于维护删边序列(链)的并查集, 一个集合的代表元就是删边序列的头部, 用 t 记录尾部, siz 记录序列长度
        int fid(int *F, int x) {return F[x]==x ? x: F[x]=fid(F,F[x]);  }
            void mg(int x, int a, int b) { a=fid(f[x],a),b=fid(f[x],b); f[x][a]=b; t[x][b]=t[x][a]; siz[x][b]+=siz[x][a]; }
            
    int bst; //这个变量用来记录当前数字能到达的标号最小的节点
    void dfs(int x)
    {   int ff=fid(f[x],fa[x]);
        	if(fa[x])
       		{
       			int ttt=fid(f[x],n+1);
        		if( !(t[x][ff]==0 && ttt==n+1 && siz[x][ff]+siz[x][ttt]!=deg[x]+2) )
                if(ff!=ttt && t[x][ttt]==n+1 && ff==fa[x]) bst=min(bst,x);
       		}
        for(int i=hd[x],y=vr[i];i;i=nt[i],y=vr[i]) if(y!=fa[x]) {
            int tt=fid(f[x],y);
            if(t[x][ff]==0 && tt==n+1 && siz[x][ff]+siz[x][tt]!=deg[x]+2) continue;
            if( ff!=tt && ff==fa[x] && t[x][tt]==y) fa[y]=x,dfs(y);
        }
    }
    
    int main()
    {
    
        int T; scanf("%d",&T);
        while(T--)
        { scanf("%d",&n);
            for(int i=1;i<=n;++i)scanf("%d",&p[i]);
          if(n==1) //特判一下
          {   puts("1");
              continue;
          }
          ct=0;
          memset(hd,0,sizeof hd); memset(deg,0,sizeof deg);
            for(int i=1,x,y; i<n; ++i)scanf("%d%d",&x,&y), ad(x,y),ad(y,x), ++deg[x],++deg[y];
          for(int i=1;i<=n;++i)
            for(int j=0;j<=n+1;++j)
                f[i][j]=t[i][j]=j, siz[i][j]=1;
           		// 以上基本都是输入初始化
          for(int i=1;i<=n;++i)
          { fa[p[i]]= 0; bst= n+2;
              dfs(p[i]);
                cout << bst << ' ';
            int y=bst, x=fa[bst], z=n+1;
            while(y)
            { mg(y,x,z);
              z=y, y=x, x=fa[x];
            }
          }
          putchar('
    ');
        }
    
    return 0;
    }
    
  • 相关阅读:
    今天面试一些程序员(新,老)手的体会
    UVA 10635 Prince and Princess
    poj 2240 Arbitrage
    poj 2253 Frogger
    poj 2485 Highways
    UVA 11258 String Partition
    UVA 11151 Longest Palindrome
    poj 1125 Stockbroker Grapevine
    poj 1789 Truck History
    poj 3259 Wormholes
  • 原文地址:https://www.cnblogs.com/tztqwq/p/13907977.html
Copyright © 2011-2022 走看看