ATcoder Grander Contest 17C Snuke and Spells 附题意

来源:互联网 发布:知乎他是谁 编辑:程序博客网 时间:2024/06/15 21:38

题目大意:

N个球,每个球上有1-N之间的数字。

如果当前有K个球,那么球上数字为K的所有球都会同时消失,然后继续进行这个操作。如果操作无法进行下去,就终止。你的任务是让所有球消失。

不难发现,很多情况下球都会剩下一些,所以你需要在开始时修改一些球上的数字,使他们能被消完。当然,修改的次数要尽可能的少,问最小修改次数。

还没完:我们有M次永久修改操作,将永久修改一个球上的数字。问每次永久修改后,最少需要修改几个球上的数字。

N<=200,000,M<=200,000

50%的数据,N<=200,M<=200

不难发现,如果要消除所有的球,必然要满足第i+1大的数和第i大的数之间,a[i]-a[i+1]=cs[a[i]](a表示数字大小,cs表示某个大小的数出现的次数)。否则,如果cs[a[i]]<a[i]-a[i+1],则将会需要修改a[i]-a[i+1]-cs[a[i]]的次数。

这个是对的,但是如果直接修改的话,复杂度是N*N*M的,只有50%的分。

我们换一下思路。如果将差值转化为覆盖的话,那么a[i]显然是能覆盖从a[i](包括a[i])开始的cs[a[i]]个数的。那么我只需要知道,每次修改后,删除的那个数所覆盖的最前一个数的被覆盖次数是否是1,和增加的那个数所覆盖的最前一个数的前一个数的被覆盖次数是否是1。那么就可以O(1)修改了。加上预处理时的差分优化,总的复杂度是O(N+M),轻松跑过。可是比赛时候连500分都没想出来。。。(我好菜啊)

附代码(好短啊)

#include<bits/stdc++.h>#define N 200100using namespace std;int n,m,ans;int cs[N],qz[N],a[N];int tot[N];int main(){scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){scanf("%d",&a[i]);cs[a[i]]++;}for(int i=1;i<=n;i++){qz[i+1]--;qz[max(i-cs[i]+1,1)]++;}int fla=0;for(int i=1;i<=n;i++){fla+=qz[i];tot[i]=fla;if(!tot[i]) ans++;}while(m--){int l,r,bas;scanf("%d%d",&l,&r);bas=a[l];if(bas-cs[bas]+1>0){if(tot[bas-cs[bas]+1]==1) ans++;tot[bas-cs[bas]+1]--;}cs[bas]--;if(r-cs[r]>0){if(tot[r-cs[r]]==0) ans--;tot[r-cs[r]]++;}cs[r]++;a[l]=r;printf("%d\n",ans);}return 0;}