• 【codeforces】940F题解


    CF Round #466的最后一题,颇有难度,正解是带修改莫队算法。

    【题意】

    给定一个长度为(n)的数组(a),并且要求执行(q)个操作,有两种不同的操作:

    ①询问一个区间([l,r])中集合(left{c_{0},c_{1},c_{2},cdots,c_{10^9} ight})的Mex,而(c_i)表示数值(i)在([l,r])中的出现次数。

    ②把(a_p)修改成(x)。

    对每一个询问输出答案。

    【题解】

    典型的区间问题,不要求在线,可以考虑莫队。

    有时间轴影响,故使用带修改莫队,时间复杂度应为(O(n^{frac{5}{3}}))。

    先对区间的移动进行分析:

    使用离散化技巧,把输入数据压缩至(n+q)的范围内。

    维护两个数组(count1,count2),(count1)记录(离散后的)每个数的出现次数,(count2)记录(count1)中的数的出现次数。

    那么所求为(count2)中第一个为0的下标位置。

    对于(count1,count2),我们都可以(O(1))维护每个操作对数组的影响,接下来考虑如何计算答案。

    (count2)数组的变动,让第一个为0的下标位置可能会有很大的跳跃,不好维护,那么我们注意到一个性质:

    答案不会超过(O(sqrt{n})),为什么呢?

    假如要将(count2_1,count2_2,cdots,count2_k)填满的话,至少需要(frac{k(k+1)}{2})个元素,可是数组的总长只有(n),所以答案必然不能太大。

    那么有了这个性质,可以暴力维护答案,维护答案的总的复杂度不会超过(O(qsqrt{n}))。

    关于莫队,还有几个需要注意的地方:

    第一个是当维护区间变化时,先考虑"伸展",再考虑"压缩",要不然会出现区间(r<l)的情况。

    一般的莫队不会太在意这个,因为后面会再加回来,但是这题中可能会导致中间结果多减了,导致(count2)数组越界。

    第二个是在带修改莫队时间轴移动上,千万不要颠倒了时间顺序,这其实也是常识了,不过我被这个卡了一会儿。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define F(i,a,b) for(int i=(a);i<=(b);++i)
    #include<cmath>
    int n,q,tim,cnt,sig,S;
    struct Qur{int x,y,t,i;}Qs[100001];
    int a[100001],b[200001],blk[100001];
    inline bool cmp(Qur p1,Qur p2){return blk[p1.x]==blk[p2.x]?(blk[p1.y]==blk[p2.y]?p1.t<p2.t:blk[p1.y]<blk[p2.y]):blk[p1.x]<blk[p2.x];}
    int p[100001],k[100001],k_[100001],ans[100001];
    int count1[200001],count2[100001];
    inline void gx1(int i){--count2[count1[i]];++count2[++count1[i]];}
    inline void gx2(int i){--count2[count1[i]];++count2[--count1[i]];}
    int main(){
    	scanf("%d%d",&n,&q); S=(int)pow(n,2.0/3.0);
    	F(i,1,n) scanf("%d",a+i), b[i]=a[i], blk[i]=(i-1)/S+1;
    	F(i,1,q){
    		int opt,x,y; scanf("%d%d%d",&opt,&x,&y);
    		if(opt==1) Qs[++cnt]=(Qur){x,y,tim,cnt};
    		else ++tim, b[n+tim]=y, p[tim]=x, k_[tim]=a[x], k[tim]=a[x]=y;
    	} F(i,1,n) a[i]=b[i];
    	sort(Qs+1,Qs+cnt+1,cmp); sort(b+1,b+n+tim+1); sig=unique(b+1,b+n+tim+1)-b-1;
    	F(i,1,n) a[i]=lower_bound(b+1,b+sig+1,a[i])-b;
    	F(i,1,tim) k[i]=lower_bound(b+1,b+sig+1,k[i])-b, k_[i]=lower_bound(b+1,b+sig+1,k_[i])-b;
    	count2[0]=sig+10;
    	int l=1, r=0, t=0;
    	F(i,1,cnt){
    		while(Qs[i].x<l) gx1(a[--l]);
    		while(Qs[i].y>r) gx1(a[++r]);
    		while(Qs[i].x>l) gx2(a[l++]);
    		while(Qs[i].y<r) gx2(a[r--]);
    		while(Qs[i].t>t) ++t, (Qs[i].x<=p[t]&&p[t]<=Qs[i].y)?gx1(k[t]),gx2(k_[t]):void(0), a[p[t]]=k[t];
    		while(Qs[i].t<t) (Qs[i].x<=p[t]&&p[t]<=Qs[i].y)?gx1(k_[t]),gx2(k[t]):void(0), a[p[t]]=k_[t], --t;
    		for(ans[Qs[i].i]=1;count2[ans[Qs[i].i]];++ans[Qs[i].i]);
    	}
    	F(i,1,cnt) printf("%d
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    C#与独孤九剑
    C#系列视频教程字符和字符串操作
    【设计模式】迪米特法则
    【设计模式】考题 模板方法模式
    C#字符和字符串
    【热门技术】解决Win7 下面很多软件安装不兼容的问题
    C#使电脑发出嗡鸣声
    C#视频教程下载(第一章)
    【设计模式】牛市股票还会亏钱 外观模式
    【设计模式】好菜每回味不同 建造者模式
  • 原文地址:https://www.cnblogs.com/PinkRabbit/p/8476664.html
Copyright © 2020-2023  润新知