【AtCoder Grand 017C】Snuke and Spells 题解 题目大意 有 n n n 个球,每个球上有数字 a i a_i a i 。 游戏是这样的:若当前还剩 k k k 个球,就把写有数字 k k k 的球全部拿走,重复这个过程。你可以修改若干球上的数字,使得最后可以拿完所有的球。求最少修改多少个球。 并且问题是动态的,有 m m m 次操作,每次会修改一个球上的数字,每次修改完后问你游戏答案。 a i ≤ n ≤ 2 × 10 5 , m ≤ 2 × 10 5 a_i \leq n \leq 2 \times 10^5,\ \ m \leq 2 \times 10^5 a i ≤ n ≤ 2 × 1 0 5 , m ≤ 2 × 1 0 5
题解 日常被欺诈。
如果数字 x x x 有 y y y 个球,我们看成 ( x − y + 1 , x ) (x-y+1, x) ( x − y + 1 , x ) 这样一条线段。那现在数轴上就有很多条线段,其中没被覆盖的位置数量就是答案。
如何证明? 首先这肯定是下界。 其次我们能构造出一种方案使其可行。若当前数轴上有空位,那就说明别的地方肯定有重复覆盖的位置。重复覆盖要么是线段相交,要么是线段包含,而这两者都必定有线段的开头被重复覆盖,因此我们一定能找到这个开头,挖掉 1 1 1 的长度来补数轴上的空位。 因此这就是最优解。
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <cstdio> #define fo(i,a,b) for(int i=a;i<=b;i++) using namespace std ; const int maxn=2e5 +5 ;int n,a[maxn],num[maxn];int ans,cov[maxn];void ADD (int x) { if (x<=0 ) return ; if (++cov[x]==1 ) ans++; } void DEC (int x) { if (x<=0 ) return ; if (--cov[x]==0 ) ans--; } int m;int main () { scanf ("%d %d" ,&n,&m); fo(i,1 ,n) { scanf ("%d" ,&a[i]); num[a[i]]++; ADD(a[i]-num[a[i]]+1 ); } while (m--) { int x,y; scanf ("%d %d" ,&x,&y); DEC(a[x]-num[a[x]]+1 ); num[a[x]]--; num[y]++; ADD(y-num[y]+1 ); a[x]=y; printf ("%d\n" ,n-ans); } }