【知识小结】分块和莫队技巧

分块

题目

P4119 [Ynoi2018]未来日记

题意
1.把区间[l,r]内所有x变成y
2.查询区间[l,r]内k小值

值域分块,经典的求k小套路:维护前缀和,一块一块得跳
这题的关键是修改,要维护值被修改成什么的映射,这个一定要想清楚!
我一直没有想清楚,写错了很多地方。
对拍了很久才过。
更多题解 orz small fat

code
洛谷测动态实际空间,良心!

CF896E Welcome home, Chtholly

题解
最巧妙的是均摊时间复杂度分析!
考虑查区间出现次数,带奇怪的修改,显然线段树不好做。应该想到分块
如何修改?
直接分两种情况讨论
如果mx >= x * 2,将[1,x]和[x + 1,2 * x]合并,再整体打减标记
如果 mx <= 2 * x , 暴力将[x + 1,mx]减去x
A(n) = T(n) - (f(n) - f(n - 1)) 令f(n) = 每一块的最大值,那么A(n) = sqrt(n) : 只有零散块重构的复杂度
而f(n)的总和显然是nsqrt(n)的

总结:
有关分块技巧:
需要维护一个并查集支持标记和值的快速合并,查找一个数的真实值即并查集的根的值。
分块把pushdown,build函数写清楚,则零散块重建就非常清晰

用宏定义写出寻找块的操作:

#define getL(x) (((x) - 1) * nsz + 1)
#define getR(x) (min(n,(x) * nsz))
#define ceil(x,y) ((x) + (y) - 1) / (y)

code

莫队

题目

bzoj 5016: [Snoi2017]一个简单的询问

这位大佬的题解写得很好, 代码也很简明

总结:
询问两个区间的关系,可以容斥到4个区间内部的关系,然后就可以莫队!
莫队的时候的写法优化:
相邻块的r一个正着排,一个倒着排。并且不用分出究竟在那块,一个数组排序就搞定,非常好写

另外
[Ynoi2016]这是我自己的发明
这道题放到树上 + 换根,拆成16个区间。有兴趣可以写一下

#include<bits/stdc++.h>
using namespace std;

#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define rvc(i,S) for(register int i = 0 ; i < (int)S.size() ; i++)
#define rvcd(i,S) for(register int i = ((int)S.size()) - 1 ; i >= 0 ; i--)
#define fore(i,x)for (register int i = head[x] ; i ; i = e[i].next)
#define forup(i,l,r) for (register int i = l ; i <= r ; i += lowbit(i))
#define fordown(i,id) for (register int i = id ; i ; i -= lowbit(i))
#define pb push_back
#define prev prev_
#define stack stack_
#define mp make_pair
#define fi first
#define se second
#define lowbit(x) ((x)&(-(x)))
#define getL(x) (((x) - 1) * nsz + 1)
#define getR(x) (min(n,(x) * nsz))
#define ceil(x,y) ((x) + (y) - 1) / (y)


typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int,int> pr;

const int inf = 1e5;
const int N = 3e6 + 10;
const int maxn = 100020;
const int BLK = 520;
const ll mod = 1e9 + 7;

int belong[maxn],nsz;
struct node{
	int r1,r2,id,f;
	node(){}
	node(int a,int b,int c,int d){
		if ( a > b ) swap(a,b);
		r1 = a,r2 = b,id = c,f = d;
	}
	bool operator < (const node a) const{
		if ( belong[r1] == belong[a.r1] ){
			if ( belong[r1] & 1 ) return r2 > a.r2;
			return r2 < a.r2;
		}
		return belong[r1] < belong[a.r1];
	}
}Q[maxn * 4];
ll ans[maxn];
int n,m,a[maxn],tot;
int cnt1[maxn],cnt2[maxn];

int main(){
	scanf("%d",&n);
	rep(i,1,n) scanf("%d",&a[i]);
	nsz = sqrt(n) + 1;
	rep(i,1,n) belong[i] = ceil(i,nsz);
	scanf("%d",&m);
	rep(i,1,m){
		int l1,r1,l2,r2;
		scanf("%d %d %d %d",&l1,&r1,&l2,&r2);
		Q[++tot] = node(r1,r2,i,1);
		if ( l1 > 1) Q[++tot] = node(l1 - 1,r2,i,-1);
		if ( l2 > 1 ) Q[++tot] = node(r1,l2 - 1,i,-1);
		if ( l1 > 1 && l2 > 1 ) Q[++tot] = node(l1 - 1,l2 - 1,i,1); 
	}
	sort(Q + 1,Q + tot + 1);
	ll now = 0; int r1 = 0 , r2 = 0;
	rep(i,1,tot){
		int R1 = Q[i].r1 , R2 = Q[i].r2 , id = Q[i].id , f = Q[i].f;
		while ( r1 < R1 ) ++r1 , cnt1[a[r1]]++ , now += cnt2[a[r1]];
		while ( r1 > R1 ) cnt1[a[r1]]-- , now -= cnt2[a[r1]] , --r1;
		while ( r2 < R2 ) ++r2 , cnt2[a[r2]]++ , now += cnt1[a[r2]];
		while ( r2 > R2 ) cnt2[a[r2]]-- , now -= cnt1[a[r2]] , --r2;
		ans[id] += now * f;
	}
	rep(i,1,m) printf("%lld\n",ans[i]);
}
P5072 [Ynoi2015]盼君勿忘

题解戳这里
推式子+莫队。
注意sqrt(n)预处理、O(1)查询的快速幂。
还有不同出现次数最多sqrt(n)种
链表的删除和插入要注意

懒得优化常数,本机1.2s ?

#include<bits/stdc++.h>
using namespace std;

#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define rvc(i,S) for(register int i = 0 ; i < (int)S.size() ; i++)
#define rvcd(i,S) for(register int i = ((int)S.size()) - 1 ; i >= 0 ; i--)
#define fore(i,x)for (register int i = head[x] ; i ; i = e[i].next)
#define forup(i,l,r) for (register int i = l ; i <= r ; i += lowbit(i))
#define fordown(i,id) for (register int i = id ; i ; i -= lowbit(i))
#define pb push_back
#define prev prev_
#define stack stack_
#define mp make_pair
#define fi first
#define se second
#define lowbit(x) ((x)&(-(x)))
#define getL(x) (((x) - 1) * nsz + 1)
#define getR(x) (min(n,(x) * nsz))
#define ceil(x,y) ((x) + (y) - 1) / (y)

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int,int> pr;

const int inf = 1e5;
const int N = 3e6 + 10;
const int maxn = 100020;
const int BLK = 520;
ll mod = 1e9 + 7;

int nsz,belong[maxn];
struct node{
	int l,r,p,id;
	bool operator < (node a)const{
		if ( belong[l] == belong[a.l] ){
			if ( belong[l] & 1 ) return r < a.r;
			return r > a.r;
		}
		return belong[l] < belong[a.l];
	}
}dt[maxn];
int a[maxn],n,m,b[maxn];

int head,nxt[maxn],pre[maxn],cnt[maxn],num[maxn];
ll pow2[maxn],pow2_s[maxn],val[maxn],ans[maxn];

void init(){
	sort(b + 1,b + n + 1);
	rep(i,1,n) a[i] = lower_bound(b + 1,b + n + 1,a[i]) - b;
	nsz = sqrt(n) + 1;
	rep(i,1,n) belong[i] = ceil(i,nsz);
//	rep(i,1,n) val[0] += b[i];
//	num[0] = n , head = 0;
}

inline void del(int c){
	if ( c == head ){
		head = nxt[c] , pre[nxt[c]] = 0;
		pre[c] = nxt[c] = 0;
	}
	else{
		if ( nxt[c] ) pre[nxt[c]] = pre[c];
		nxt[pre[c]] = nxt[c];
		pre[c] = nxt[c] = 0;
	}
}
inline void insert(int d){
	cnt[d]++;
	int c = cnt[d];
	val[c] += b[d] , val[c - 1] -= b[d];
	num[c]++ , num[c - 1]--;
	if ( num[c] == 1 ){
		if ( c - 1 ){
			nxt[c] = nxt[c - 1];
			pre[c] = c - 1;
			if ( nxt[c - 1] ) pre[nxt[c - 1]] = c;
			nxt[c - 1] = c;
		}
		else{
			nxt[c] = head;
			if ( head ) pre[head] = c;
			head = c , pre[c] = 0;
		}
	}
	if ( c > 1 && !num[c - 1] ){
		del(c - 1);
	}
}
inline void remove(int d){
	cnt[d]--;
	int c = cnt[d];
	val[c] += b[d] , val[c + 1] -= b[d];
	num[c]++ , num[c + 1]--;
	if ( c && num[c] == 1 ){
		if ( !pre[c + 1] ) head = c;
		else pre[c] = pre[c + 1] , nxt[pre[c]] = c;
		pre[c + 1] = c;
		nxt[c] = c + 1;
	}
	if ( !num[c + 1] ){
		del(c + 1);
	}
}
inline void up(ll &x,ll y){ x = (x + y + mod) % mod; }
inline ll power(int k){
	return pow2[k / nsz] * pow2_s[k - k / nsz * nsz] % mod;
}
int ccnt = 0;
inline ll calc(int len){
	ll res = 0;
	pre[0] = nxt[0] = 0;
	for (register int i = head ; i ; i = nxt[i]){
		++ccnt;
		//cout<<i<<" "<<val[i]<<endl;
		up(res,val[i] % mod * (power(len) - power(len - i)));
	}
	return (res % mod + mod) % mod;
}
int main(){
//	freopen("input.txt","r",stdin);

	scanf("%d %d",&n,&m);
	rep(i,1,n) scanf("%d",&a[i]) , b[i] = a[i];
	
	init();
	rep(i,1,m){
		scanf("%d %d %d",&dt[i].l,&dt[i].r,&dt[i].p);
		dt[i].id = i;
	}
	sort(dt + 1,dt + m + 1);
	int curl = 1 , curr = 0;
	rep(i,1,m){
		int L = dt[i].l , R = dt[i].r , p = dt[i].p; mod = p;
		pow2[0] = pow2_s[0] = 1;
		rep(j,1,nsz) pow2_s[j] = pow2_s[j - 1] * 2 % p;
		pow2[1] = pow2_s[nsz];
		rep(j,2,ceil(n,nsz)) pow2[j] = pow2[j - 1] * pow2[1] % p;
		while ( curl < L ) remove(a[curl++]);
		while ( curl > L ) insert(a[--curl]);
		while ( curr < R ) insert(a[++curr]);
		while ( curr > R ) remove(a[curr--]);
		//
		//cout<<"check\n";
		ll d = calc(R - L + 1);
		ans[dt[i].id] = d;
		//cout<<L<<" "<<" "<<R<<" "<<d<<endl;
		//cerr<<ccnt<<endl;
	}
	rep(i,1,m) printf("%lld\n",ans[i]);

}**
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值