洛谷 P4396 [AHOI2013]作业(莫队 + 分块 + 根号均摊)

耗尽温柔 提交于 2020-01-27 21:54:18

在这里插入图片描述


很显然有一个莫队套树状数组的做法, 因为树状数组更新和查询的复杂度都是 logn\log n,复杂度是 nmlognn \sqrt m \log n

通过学习了解到分块数据结构可以做到 O(1)O(1) 查询,O(n)O(\sqrt n) 更新 与 O(n)O(\sqrt n) 查询,O(1)O(1) 更新的平衡。

由于 查询是 10610^6 级别,通过分块数据结构 和 根号平衡在莫队中更新可以做到 O(1)O(1),总体复杂度为 O(mn+nm)O(m\sqrt n + n \sqrt m) ,理论上限大约为 31083*10^8 左右

做法是对权值分块,维护块内数字出现的次数和以及不同数字的个数。


详细见代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
int n,m,a[maxn],block,cnt[maxn];
int ans[maxn],res[maxn];
struct node {
	int id,l,r,a,b;
	node(int idi = 0,int li = 0,int ri = 0,int ai = 0,int bi = 0) {
		id = idi;
		l = li;
		r = ri;
		a = ai;
		b = bi;
	}
	bool operator < (const node &rhs) const {
		return l / block == rhs.l / block ? r < rhs.r : l < rhs.l;
	} 
}q[maxn];
struct Block {
	int siz,num,p[maxn],sum[maxn],t[maxn],tot[maxn];
	void init() {
		siz = sqrt(n);
		num = n / siz + (n % siz > 0);
		memset(p,0,sizeof p);
		memset(t,0,sizeof t);
		memset(sum,0,sizeof sum);
		memset(tot,0,sizeof tot);
	}
	void update(int pos,int v) {
		int tp = pos / siz + (pos % siz > 0);
		p[pos] += v;
		sum[tp] += v;
	}
	void modify(int pos,int v) {
		int tp = pos / siz + (pos % siz > 0);
		t[pos] += v;
		tot[tp] += v;
	}
	int ask(int l,int r) {
		int lp = l / siz + (l % siz > 0);
		int rp = r / siz + (r % siz > 0);
		int ans = 0;
		for(int i = lp + 1; i < rp; i++)
			ans += sum[i];
		if(lp == rp) {
			for(int i = l; i <= r; i++)
				ans += p[i];
		} else {
			for(int i = l; i <= lp * siz; i++)
				ans += p[i];
			for(int i = (rp - 1) * siz + 1; i <= r; i++)
				ans += p[i];
		}
		return ans;
	}
	int qry(int l,int r) {
		int lp = l / siz + (l % siz > 0);
		int rp = r / siz + (r % siz > 0);
		int ans = 0;
		for(int i = lp + 1; i < rp; i++)
			ans += tot[i];
		if(lp == rp) {
			for(int i = l; i <= r; i++)
				ans += t[i];
		} else {
			for(int i = l; i <= lp * siz; i++)
				ans += t[i];
			for(int i = (rp - 1) * siz + 1; i <= r; i++)
				ans += t[i];
		}
		return ans;
	}
}B;
int curleft,curright;
void modify(int l,int r) {
	while(curleft < l) {
		B.update(a[curleft],-1);
		cnt[a[curleft]]--;
		if(cnt[a[curleft]] == 0) B.modify(a[curleft],-1);
		curleft++;
	}
	while(curleft > l) {
		curleft--;
		cnt[a[curleft]]++;
		B.update(a[curleft],1);
		if(cnt[a[curleft]] == 1) B.modify(a[curleft],1);
	}
	
	while(curright < r) {
		curright++;
		cnt[a[curright]]++;
		B.update(a[curright],1);
		if(cnt[a[curright]] == 1) B.modify(a[curright],1);
	}
	while(curright > r) {
		cnt[a[curright]]--;
		B.update(a[curright],-1);
		if(cnt[a[curright]] == 0) B.modify(a[curright],-1);
		curright--;
	}
}
int main() {
	scanf("%d%d",&n,&m);
	B.init();
	for(int i = 1; i <= n; i++) {
		scanf("%d",&a[i]);
	}
	for(int i = 1,l,r,a,b; i <= m; i++) {
		scanf("%d%d%d%d",&l,&r,&a,&b);
		q[i] = node(i,l,r,a,b);
	}
	block = sqrt(n);
	sort(q + 1,q + m + 1);
	curleft = curright = 0;
	for(int i = 1; i <= m; i++) {
		modify(q[i].l,q[i].r);
		int j = q[i].id;
		ans[j] = B.ask(q[i].a,q[i].b); 
		res[j] = B.qry(q[i].a,q[i].b);
	}
	for(int i = 1; i <= m; i++)
		printf("%d %d\n",ans[i],res[i]);
	return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!