Middle Duplication (CFD2E) (贪心,中序树,字典序大小.dfs)

 

 大佬的思路:

 

 

#include<bits/stdc++.h>
using namespace std;
int n,k,l[200010],r[200010],pos[200010];
char c[200010];
vector<int>seq;
void precalc(int u)
{
    if(l[u])precalc(l[u]);
    seq.push_back(u);
    if(r[u])precalc(r[u]);
}
bool good[200010],isDup[200010];
void dfs(int u,int cost)
{
    if(!u||cost>k)return;
    dfs(l[u],cost+1);
    if(isDup[l[u]])isDup[u]=1;
    else if(good[u])isDup[u]=1,k-=cost;
    if(isDup[u])dfs(r[u],1);
}
int main()
{
    scanf("%d%d",&n,&k);
    scanf("%s",c+1);
    for(int i=1;i<=n;i++)scanf("%d%d",&l[i],&r[i]);
    precalc(1);
    char lst=c[seq.back()];
    for(int i=n-2;i>=0;i--)
    {
        int u=seq[i],v=seq[i+1];
        if(c[u]!=c[v])lst=c[v];
        if(c[u]<lst)good[u]=1;
    }
    dfs(1,1);
    for(auto u:seq)
    {
        putchar(c[u]);
        if(isDup[u])putchar(c[u]);
    }
    return 0;
}
View Code

后记:

  • 他本身就是要中序后的一个字典序的大小
  • 于是看看中序排序后的序列是什么, 然后根据这个序列看看那个点是不是需要复制 (比后面第一个不同的数小)
  • 在上面预处完后开始dfs, 更具字典序性子
  • 左边优先级最高,然后就是中间吗,在是右边, 
  • 注意这个是更新时的前提时父亲也要加倍, 所以中间不行时,右边一定也不行

题目分析

我们首先进行一次中序遍历,找到每个节点在中序遍历中所处的位置。

接下来,我们能知道每个节点被复制是否会让字典序更小。只需要对比它和在中序遍历中的下一个和它不同的字符的大小即可。

显然,我们会在能让字典序变小的节点中尽量选择靠前的,即左子树比右子树优先。同时,不难证明:

  • 如果当前节点被复制不会使答案更优,则它的右子树一定不会被复制。
  • 在左右子树都能使答案更优的前提下,优先复制左子树。

因此,我们可以执行一个 dfs 框架:

  • 假设当前访问的节点为 u,复制该节点的代价为 ����cost
  • 若 �=0u=0 或 ����>�cost>k,返回。
  • 访问当前节点的左儿子 ��lu,同时将 ����←����+1costcost+1
  • 如果 ��lu 需要被复制,则 u 需要被复制。
  • 否则,在 ��lu 不需要被复制的情况下,如果 u 被复制能让答案更优,则 u 也需要被复制,且 �←�−����kkcost
  • 如果当前节点需要被复制,访问当前节点的右儿子 ��ru,并将 ����←1cost1

这样,我们就找到了每个节点是否需要被复制。时间复杂度 �(�)O(n)

posted @ 2023-03-08 23:24  VxiaohuanV  阅读(15)  评论(0编辑  收藏  举报