zoukankan      html  css  js  c++  java
  • AGC017C Snuke and Spells(巧妙的线段覆盖模型)

    题目大意:

    给出n个球,每个球上都有数字,然后每次都进行如下操作

    如果当前的球总共有k个,那么就把球上数字为k的所有球都消除掉

    注意到,并不是每种情况都可以全部消光,所以你可以选择若干球,把它们标号改变,最后达到消光的目的

    问最少需要改变几个球。

    后面还跟着m个询问,每个询问会改变一个球的标号,问改变之后最少需要改变几个球才能消光。

    题解:

    大体先构建一个线段覆盖的模型,然后再证明这个模型是正确的

    对于标号为i的球,覆盖线段[i-Ni, i](Ni为标号为i的球的个数)

    每个球都做这样的覆盖,最后看[0, n]这段有多少没被覆盖的线段,有多少就是需要改变多少个球

    证明:

    首先如果线段全覆盖了,那么就不需要改变任何一个球

    如果线段没有全覆盖,那么我们就需要改变一个球的标号i变成标号j

    这样会使标号为i的线段覆盖减少1格,标号为j的线段的覆盖增加1格

    那么每次最多只会减少一个没被覆盖的线段

    所以最少就需要那么多球改变

    根据这个模型,就很好写代码了

    #include <iostream>
    #include <cstdio>
    #define fi first
    #define se second
    using namespace std;
    const int maxn = 2e5 + 100;
    typedef pair<int, int> PII;
    PII q[maxn];
    int cnt[maxn], f[maxn], a[maxn];
    int main()
    {
        int n, m;
        cin>>n>>m;
        for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
        for(int i = 1; i <= m; i++) scanf("%d %d", &q[i].fi, &q[i].se);
        for(int i = 1; i <= n; i++) cnt[a[i]]++;
        for(int i = 1; i <= n; i++){
            if(cnt[i]){
                for(int j = max(0, i-cnt[i]); j < i; j++) f[j]++;
            }
        }
        int ans = 0;
        for(int i = 0; i < n; i++) if(!f[i]) ans++;
        for(int i = 1; i <= m; i++){
            int x = a[q[i].fi], y = q[i].se;
            a[q[i].fi] = y;
            if(x-cnt[x] >= 0){
                f[x-cnt[x]]--;
                if(f[x-cnt[x]] == 0) ans++;
            }
            cnt[x]--;
            if(y-cnt[y]-1 >= 0){
                f[y-cnt[y]-1]++;
                if(f[y-cnt[y]-1] == 1) ans--;
            }
            cnt[y]++;
            printf("%d
    ", ans);
        }
    }
  • 相关阅读:
    ES6-->ECMAScript 6.0 新增方法,一些基本语法
    初识 Nodejs (了解Nodejs)
    Vue框架初识
    python语法入门之流程控制
    python中基本运算符
    格式化输出
    基本数据类型
    变量,解释器,垃圾回收机制,小整数池总结
    编程语言发展史
    计算机基础
  • 原文地址:https://www.cnblogs.com/Saurus/p/7207771.html
Copyright © 2011-2022 走看看