AGC017C Snuke and Spells(巧妙的线段覆盖模型)
Posted Saurus
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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\n", ans); } }
以上是关于AGC017C Snuke and Spells(巧妙的线段覆盖模型)的主要内容,如果未能解决你的问题,请参考以下文章
AGC31E Snuke the Phantom Thief
「题解」agc031_e Snuke the Phantom Thief