• 【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;
    }
    
  • 相关阅读:
    序列化
    cookie 和 session
    a 标签提交表单
    SpringBoot使用Easypoi导出excel示例
    PDF操作类库 iText
    HandlerInterceptor
    Fastdfs
    InitializingBean
    CORS CorsFilter
    XMLHttpRequest
  • 原文地址:https://www.cnblogs.com/Qing-LKY/p/CF1481C-solution.html
Copyright © 2020-2023  润新知