【CF1481C】Fence Painting 题解

哈哈哈哈哈看了一眼 tutorial 的评论区好多人在喷前四题哈哈哈哈哈哈

原题链接

题意简介

你有一条有 n 块木板的栅栏。这些木板原本的颜色分别是 \(A_i\),现在你希望把它们涂成 \(B_i\),为此你请来了一个祖宗师傅。这个师傅非常霸道,他每刷一次颜料,只会涂一块木板,并且刷了就一定要涂。他只刷 m 次,刷颜料的顺序还是固定的,如第 i 次刷的颜色是 \(C_i\),然后他会将某个木板的颜色涂改成 \(C_i\)。现问:如果你可以指定师傅每次涂哪个木板,你能否使最终的栅栏颜色序列变成 \(B_i\)?如果可以,输出师傅每次要涂哪个木板。

(感觉我这一翻译理解起来变复杂了)

简单说就是你需要把序列 A 改成序列 B,但是你只能依次修改 m 次,每次将任意位置的数字改成 \(C_i\),允许重复覆盖但是不允许跳过。问你是否可以达成目标,如果可以,输出 m 次修改的位置。

思路分析

跟 D 题一样的分类讨论+构造方案,冷静下来,多注意下细节,应该是能写的。本暴躁老哥就为了初始化把 m 打成 n 查了半小时。

首先,考虑什么情况下会输出 No。

  1. 某种颜色的操作数量不足。
  2. 操作数量是够的,但有一些多余的操作无处安放,会覆盖掉已经改好的位置。

自然的,我们会想到:去统计需要的颜色的种类和数量,与操作中出现的颜色的种类和数量进行对比。

(为了方便输出修改的位置,我们可以用 vector 或链表、邻接表之类存下这些颜色对应了哪些需要修改的位置。)

但是,并不是所有的操作都能恰好用上,总有一些操作是无效的,我们需要想办法处理这些多余的操作。

于是,我们自然想到,只要让它们被后面的有效的操作覆盖掉,或是浪费在与自己相同颜色的位置上就行了。(只要保证它们不会对最终成形的 B 产生影响即可)

具体的细节可以看代码。考虑仔细一点,查的耐心一点应该就没问题。

代码库

#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int N=1e5+5;
int t,n,m,A[N],B[N],C[N],cnt[N],qnt[N]; int vis[N];
vector<int> Q[N];
int ans[N];
int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) vis[i]=0,Q[i].clear();
        for(int i=1;i<=n;i++) scanf("%d",A+i),cnt[i]=qnt[i]=0;
        for(int i=1;i<=m;i++) ans[i]=0;
        for(int i=1;i<=n;i++){
            scanf("%d",B+i);
            if(B[i]!=A[i]) cnt[B[i]]++,Q[B[i]].push_back(i); 
            // cnt 表示某种颜色需要的数量 Q 用来存放这种颜色需要修改的位置
            if(B[i]==A[i]) vis[A[i]]=i;
            // vis 表示不需要修改的,可以用来覆盖掉多余的操作的位置
        }
        int mp=0,mlost=0; 
        // mp 记录可以确认修改位置的最后一个操作 mlost 则记录无法确认的最后一个操作
        for(int i=1;i<=m;i++){
            scanf("%d",C+i);
            qnt[C[i]]++; 
            // qnt 表示某种颜色的操作的数量
            if(cnt[C[i]]==0){ 
                // 完全不需要的颜色只能覆盖掉
                if(vis[C[i]]>0) ans[i]=vis[C[i]],mp=i; 
                else mlost=i; // 找不到该改哪,只能等后面覆盖在一个将会被覆盖的位置上
            }else if(qnt[C[i]]<=cnt[C[i]]){
                // 需求还在
                mp=i;
                ans[i]=Q[C[i]][qnt[C[i]]-1];
            }else{
                // 过剩的需求可以直接覆盖在已经被改好了的位置上 反正是同一个颜色
                mp=i;
                ans[i]=Q[C[i]][0];
            }
        }
        bool fail=0;
        for(int i=1;i<=n;i++) if(cnt[i]>qnt[i]){ fail=1; break; } // 某种颜色的需求没被满足
        if(fail||mp<mlost){ puts("NO"); continue; } // mlost 的操作会制造多余的颜色破坏掉改好的序列
        puts("YES");
        for(int i=1;i<=m;i++) if(!ans[i]) ans[i]=ans[mp]; 
        // 反正 mp 在后,那就让所有多余的操作指向 mp 会修改的位置就行了 这样 mp 会把它们覆盖掉
        for(int i=1;i<=m;i++) printf("%d ",ans[i]);
        puts("");
    }
    return 0;
}
posted @ 2021-02-06 22:16  喵乖乖喵  阅读(208)  评论(0编辑  收藏  举报

膜拜众神

网安院技术部     ZZY大师     Xinyang 大佬     Wjyyy