Codeforces 1476G - Minimum Difference(带修莫队+根号平衡)
震惊!我竟然独立切掉了这道 *3100 的题!
虽然此题难度的确虚高,感觉真实评分也就 2800~2900 罢。但感觉还是挺有成就感的(
注意到题目中所询问的那坨东西基于每个数在区间中出现的次数,因此此题必不可少的一个步骤就是求出每个数的出现次数 \(cnt_x\),这个可以用带修莫队在 \(n^{5/3}\) 的时间内求出,这里就不再赘述了。
接下来考虑怎样计算答案,这也是本题的难点所在。我们将所有 \([l,r]\) 中出现次数非零的数的出现次数从小到大排序,设为 \(x_1,x_2,\cdots,x_m\),显然我们选择的数在 \(x\) 序列中一定是一段连续的区间 \(x_i,x_{i+1},\cdots,x_{i+k-1}\),故答案为 \(\min\limits_{i=1}^{m-k+1}x_{i+k-1}-x_i\)。
暴力排序求解显然是不行的,但是注意到 \(x\) 数组有一个非常重要的性质,就是 \(\sum\limits_{i=1}^mx_i=r-l+1\le n\),我们不妨设一个阈值 \(B\),我们考虑以下两个暴力:
- 对于 \(x_{i+k-1}\le B\) 的 \(i\),我们开一个桶 \(cnt\_cnt_i\) 表示 \(\sum[x_j=i]\),也就是 \(x_j=i\) 的个数,然后枚举 \(x_i\),用 two pointers 找出最小的 \(r\) 使得 \(\sum\limits_{j=i}^rcnt\_cnt_j\ge k\) 并用 \(r-i\) 更新答案。
- 对于 \(x_{i+k-1}>B\) 的 \(i\),注意到这样的 \(i\) 不超过 \(\lfloor\dfrac{n}{B}\rfloor\) 个,于是我们暴力找出 \(x_i>B\) 的 \(x_i\) 并将其从小到大排序,然后直接用 \(x_{i+k-1}-x_i\) 更新答案即可。复杂度 \(\dfrac{n}{B}\log\dfrac{n}{B}\)。
用均值不等式可知 \(B=\sqrt{n\log n}\) 时复杂度最优。
还有一个小问题,就是怎样求出 \(x_{i}>B\) 的 \(x_i\),一个很显然的思路是在莫队 push/pop 的过程中维护一个 set,如果当前加入的值的 \(cnt\ge B\) 就压入 set,否则从 set 中删除当前值,不过这样会使带修莫队的复杂度变为 \(n^{5/3}\log n\),无法通过。注意到如果 \(i\) 满足 \(i\) 从始至终在序列中出现的次数 \(<B\),那么它一定不可能出现在这个 set 中,故考虑预处理所有存在某个时刻在序列中出现次数 \(>B\) 的 \(i\)——这样的 \(i\) 必定不会超过 \(\dfrac{n}{B}\),然后每次询问时暴力在这样的 \(i\) 中查找出现次数 \(>B\) 的值,这样就能保证复杂度了。
时间复杂度 \(n^{5/3}+n\sqrt{n\log n}\)
还有一点是回滚莫队时间轴那一维是先更新再移动指针,我因此 WA 了好几发……
这个莫队板子题能放到 edu 的 G 就 nm 离谱
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
namespace fastio{
	#define FILE_SIZE 1<<23
	char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
	inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
	inline void putc(char x){(*p3++=x);}
	template<typename T> void read(T &x){
		x=0;char c=getchar();T neg=0;
		while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
		while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
		if(neg) x=(~x)+1;
	}
	template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
	template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
	void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MAXN=1e5;
const int BLK_2_3=2000;
const int BLK_1_2=316;
const int INF=0x3f3f3f3f;
int n,qu,qk=0,a[MAXN+5],tot_cnt[MAXN+5],cnt[MAXN+5],cnt_cnt[MAXN+5];
int blk_cnt,L[MAXN+5],R[MAXN+5],bel[MAXN+5];
struct chg{int x,pre,cur;} c[MAXN+5];
vector<int> tot;
void push(int x){cnt_cnt[cnt[x]]--;cnt[x]++;cnt_cnt[cnt[x]]++;}
void pop(int x){cnt_cnt[cnt[x]]--;cnt[x]--;cnt_cnt[cnt[x]]++;}
struct query{
	int l,r,k,id;
	bool operator <(const query &rhs){
		if(bel[l]!=bel[rhs.l]) return l<rhs.l;
		else if(bel[r]!=bel[rhs.r]) return r<rhs.r;
		return id<rhs.id;
	}
} q[MAXN+5];
int ans[MAXN+5];
int deal(int k){
	vector<int> over;int ret=INF;
	for(int i=0;i<tot.size();i++) if(cnt[tot[i]]>BLK_1_2) over.pb(cnt[tot[i]]);
	sort(over.begin(),over.end());
	for(int l=1,r=1,sum=0;l<=BLK_1_2;l++){
		while(r<=BLK_1_2&&sum<k) sum+=cnt_cnt[r],r++;
		if(sum<k){
			int need=k-sum;
			if(need<=over.size()) chkmin(ret,over[need-1]-l);
		} else chkmin(ret,r-l-1);
		sum-=cnt_cnt[l];
	}
	for(int i=0;i+k-1<over.size();i++) chkmin(ret,over[i+k-1]-over[i]);
	return ret;
}
int main(){
	scanf("%d%d",&n,&qu);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),tot_cnt[a[i]]++;
	blk_cnt=(n-1)/BLK_2_3+1;
	for(int i=1;i<=blk_cnt;i++){
		L[i]=(i-1)*BLK_2_3+1;R[i]=min(i*BLK_2_3,n);
		for(int j=L[i];j<=R[i];j++) bel[j]=i;
	}
	for(int i=1;i<=qu;i++){
		int opt;scanf("%d",&opt);
		if(opt==1){
			qk++;scanf("%d%d%d",&q[qk].l,&q[qk].r,&q[qk].k);
			q[qk].id=i;
		} else {
			scanf("%d%d",&c[i].x,&c[i].cur);
			c[i].pre=a[c[i].x];a[c[i].x]=c[i].cur;
			tot_cnt[c[i].cur]++;
		}
	} sort(q+1,q+qk+1);int cl=1,cr=0,ct=qu;
	memset(ans,-1,sizeof(ans));
	for(int i=1;i<=MAXN;i++) if(tot_cnt[i]>BLK_1_2) tot.pb(i);
	for(int i=1;i<=qk;i++){
		while(cr<q[i].r) push(a[++cr]);
		while(cl>q[i].l) push(a[--cl]);
		while(cr>q[i].r) pop(a[cr--]);
		while(cl<q[i].l) pop(a[cl++]);
		while(ct<q[i].id){
			if(c[ct].x){
				if(q[i].l<=c[ct].x&&c[ct].x<=q[i].r) pop(c[ct].pre);
				a[c[ct].x]=c[ct].cur;
				if(q[i].l<=c[ct].x&&c[ct].x<=q[i].r) push(c[ct].cur);
			} ct++;
		}
		while(ct>q[i].id){
			if(c[ct].x){
				if(q[i].l<=c[ct].x&&c[ct].x<=q[i].r) pop(c[ct].cur);
				a[c[ct].x]=c[ct].pre;
				if(q[i].l<=c[ct].x&&c[ct].x<=q[i].r) push(c[ct].pre);
			} ct--;
		} ans[q[i].id]=deal(q[i].k);
	}
	for(int i=1;i<=qu;i++) if(~ans[i]) printf("%d\n",(ans[i]^INF)?ans[i]:-1);
	return 0;
}
/*
12 11
2 1 1 2 1 1 3 2 1 1 3 3
1 2 10 3
1 2 11 3
2 7 2
1 3 9 2
1 1 12 1
1 1 12 4
2 12 4
1 1 12 4
2 1 5
1 3 12 2
1 1 4 3
*/
/*
10 2
1 2 2 1 1 3 1 1 3 1
1 3 8 2
1 3 8 3
*/
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号