【codeforces】940F题解

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

【题意】

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

①询问一个区间\([l,r]\)中集合\(\left\{c_{0},c_{1},c_{2},\cdots,c_{10^9}\right\}\)的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(q\sqrt{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\n",ans[i]);
	return 0;
}

原文地址:https://www.cnblogs.com/PinkRabbit/p/8476664.html

时间: 2024-08-30 15:59:56

【codeforces】940F题解的相关文章

CodeForces Dubstep 题解

Vasya works as a DJ in the best Berland nightclub, and he often uses dubstep music in his performance. Recently, he has decided to take a couple of old songs and make dubstep remixes from them. Let's assume that a song consists of some number of word

codeforces Towers 题解

Little Vasya has received a young builder's kit. The kit consists of several wooden bars, the lengths of all of them are known. The bars can be put one on the top of the other if their lengths are the same. Vasya wants to construct the minimal number

codeforces 1296 题解(更新中)

codeforces 1296 题解 A. Array with Odd Sum 想要数组加和为奇数,可以从数组长度是奇数还是偶数着手 若数组长度为偶数,则数组全奇或全偶必定不能构造满足题目要求的数列 若数组长度为奇数,则数组全偶必定不能构造满足题目要求的数列 #include <bits/stdc++.h> using namespace std; typedef long long ll; typedef double db; #define _for(i,a,b) for(int i =

codeforces 1303 题解(更新中)

codeforces 1303 题解 A. Erasing Zeroes 想让字符串中的 \(1\) 连续,而我们能做的只有删 \(0\) ,则需要删去除开头以及结尾外的 所有 \(0\) 块.所以从头扫一遍统计开头 \(0\) 块,从尾扫一遍统计结尾 \(0\) 块,再用 \(0\) 的数量减去这两部分即可,可能为负所以跟 \(0\) 取最大值. 时间复杂度 \(O(n)\) #include <bits/stdc++.h> using namespace std; typedef long

codeforces#536题解

CodeForces#536 A. Lunar New Year and Cross Counting Description: Lunar New Year is approaching, and you bought a matrix with lots of "crosses". This matrix \(M\) of size \(n \times n\) contains only 'X' and '.' (without quotes). The element in t

CodeForces 281 题解

A题: 题意:给出按照时间顺序的比赛记录,比赛记录了哪一分钟有哪位球员得到了黄牌或红牌,输出罚下的人的序列. 题解:直接按照时间读入模拟就可..注意坑在有可能一位球员罚下后又得到黄牌或红牌,这时候不应再输出这个人了. B题: 题意:给出两个摔跤选手每个动作的得分,正数为第一个人得分负数为第二个人得分,总分高者胜,若相同则“字典序”较大的获胜,再相同则最后得分的人获胜. 题解:直接模拟就可..要用long long.. C题: 题意:两个队伍篮球比赛,给出每个队伍投进的每个球距离球框的距离,现在要

codeforces - 448C 题解

题意:给定一个栅栏,每次涂一行或者一列,问最少几次能够涂完 题解:分治算法+DP思想,每次的状态从竖着涂和横着涂中选择,同时向更高的部分递归计算. 1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<climits> 5 using namespace std; 6 int a[5001]; 7 int f[5001][5001]; 8 void cal(int l

codeforces - 148E 题解

题目大意:一个公主有一个摆满瓷器的架子,她生气的时候就要打碎m个瓷器.这个架子有n层,每层的瓷器每次只能从最左边拿或者从最右边拿,问打碎的瓷器的最大价值. 题解:这是一个泛化物品+分组背包的DP,首先将每一层上拿出瓷器的方案作为一个物品,拿出瓷器的方案的代价是瓷器数量,价值是这一层上所有方案中最大的价值. 首先为了计算方案的时候可以快速计算,我们在读入的时候就计算出前缀和sum,然后在计算方案的时候,每一层上打碎的数量对应一个物品,然后枚举总数量分配到两侧打碎的价格,得到最大价值.最后使用分组背

Codeforces 985B 题解

题意 有$n$个开关和$m$盏灯,给定一个矩阵$a(a_{i,j}\in [0,1])$,若$a_{i,j}=1$则说明开关$i$与第$j$盏灯连接.初始时所有灯都是关闭的.按下某开关后,所有与这个开关连接的灯将会打开,已经打开的不会关闭. 现在你需要去掉一个开关,使得按下剩余$n-1$个开关后,所有灯都可以打开.若存在方案,则输出YES,否则输出NO. 题解 直观的想法为,枚举去掉的开关$i$,然后枚举每个与开关$i$相连的灯$j$,若存在一个$j$只能靠开关$i$打开(即矩阵第$j$列只有第