带有?的莫队

写莫队如同吸毒,易上头(误

莫队是一种用来统计(注意不是维护)区间各种东西的数据结构,由于其短小精悍,拓展性强而让人欲罢不能

常用的莫队有四种,其中树上莫队有个前置芝士要点,这里只会简单提一下

列表

  1. 普通莫队
  2. 带修莫队
  3. 回滚莫队
  4. 树上莫队

一个一个来讲

普通莫队

本质思想是继承前面的状态

A:举个栗子?

Q:假设我们已经处理好了区间[l , r],
特殊化的,对于下一个区间[l , r’],我们只需要统计[r + 1 , r’]即可
推广到一般,对于下一个区间[l’ , r’],我们处理[l , l’]以及[r , r’]即可

具体实现维护两个指针l , r即可

莫队的基操就是这个,不过我们改一下询问的处理顺序。
具体的,我们将左端点按块进行排序,同块按右端点排序,这样就能时间复杂度从O(n 2) 降到 O(n3/2).

例题以后再补

带修莫队

看起来很高大山,但说白了就是加多一个时间维,把两个指针(l , r)变成(l , r , t),且优先维护t

结合例题:P1903 [国家集训队]数颜色

我们将修改和查询分成两个数组,跑莫队时优先处理时间维t,然后再处理l和r

具体实现有个小trick,详见树上带修莫队的例题

回滚莫队

在脑子里想象下沙滩,那个潮是不是上来之后又会回去?

把这一块的l想象成潮就好了

具体的只继承r的答案,l贡献的答案扔掉

变相省掉了一个删除的操作

结合例题:AT1219 歴史の研究

这里贴一下代码

code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int N = 200010;
typedef long long ll;
struct query
{
	int l , r;
	int id;
} b[N];
int n , m , len;
int bnum , bl[N] , br[N] , bel[N] , LEN;
ll ans[N] , a[N] , num[N];
ll cnt[N] , cnt2[N] , typ[N];
int find(ll x)
{
	int l = 1 , r = len , mid , best = len;
	while(l <= r)
	{
		mid = (l + r) >> 1;
		if(num[mid] >= x) best = mid , r = mid - 1;
		else l = mid + 1; 
	 } return best;
}
bool cmp(query a , query b)
{
	return (bel[a.l] ^ bel[b.l]) ? bel[a.l] < bel[b.l] : a.r < b.r;
}
int main()
{
	scanf("%d%d" , &n , &m);
	LEN = sqrt(n) , bnum = ceil((double)n / LEN);
	for(int i = 1 ; i <= bnum ; i++)
	{
		bl[i] = (i - 1) * LEN + 1;
		br[i] = i * LEN;
		for(int j = bl[i] ; j <= br[i] ; j++) bel[j] = i;
	}
	for(int i = 1 ; i <= n ; i++) scanf("%lld" , &a[i]) , num[i] = a[i];
	sort(num + 1 , num + n + 1) ; len = unique(num + 1 , num + n + 1) - num - 1;
	for(int i = 1 ; i <= n ; i++) typ[i] = find(a[i]);
	for(int i = 1 ; i <= m ; i++) scanf("%d%d" , &b[i].l , &b[i].r) , b[i].id = i;
	sort(b + 1 , b + m + 1 , cmp);
	int i = 1;
	for(int k = 0 ; k <= bnum ; k++)
	{
		int l = br[k] + 1 , r = br[k];
		ll now = 0;
		memset(cnt , 0 , sizeof cnt);
		for(; bel[b[i].l] == k ; i++)
		{
			int ql = b[i].l , qr = b[i].r;
			ll tmp;
			if(bel[ql] == bel[qr])
			{	
				tmp = 0;
				for(int j = ql ; j <= qr ; j++) cnt2[typ[j]] = 0;
				for(int j = ql ; j <= qr ; j++)
				{
					++cnt2[typ[j]] ; tmp = max(tmp , 1ll * cnt2[typ[j]] * a[j]);
				}
					
				ans[b[i].id] = tmp;
				continue;
			}
			while(r < qr)
			{
				++r ; cnt[typ[r]]++; now = max(now , 1ll * cnt[typ[r]] * a[r]);
			}
			tmp = now;
			while(l > ql)
			{
				l-- ; cnt[typ[l]]++; now = max(now , 1ll * cnt[typ[l]] * a[l]);
			}
			ans[b[i].id] = now;
			while(l < br[k] + 1)
			{
				--cnt[typ[l]] ; l++;
			 } 
			now = tmp;
		}
	}
	for(int i = 1 ; i <= m ; i++) printf("%lld\n" , ans[i]);
	return 0;
}

树上莫队

所有的莫队都是基于一维序列的

因此单纯树上的不好搞

既然树上不好搞,那我们就把它搞成序列

我们用一个叫欧拉序(前面提到的前置芝士)的东西来把这棵树拍扁

具体实现就是跑dfs序的时候,每个节点搜到跟退出的时候都记录到序列里

容易发现每个节点出现两次,而且每个相同节点中间的点就是以这个点为根的子树

代码的实现以及细节详见这篇博客

SP10707 COT2 - Count on a tree II

code:(记得处理lca)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define Orz c = getchar()
#define sto c < '0' || c > '9'
#define StmAKioi c >= '0' && c <= '9'
#define yzhyyds return ret
using namespace std;
const int N = 200010 , M = 300010 , K = 20;
int h[N] , to[M] , nxt[M] , tot = 1;
int a[N] , n , m , bel[N] , LEN , gg[N];
int fir[N] , las[N] , idx;
int dep[N] , dp[N][K] , dfn[N];
int col[N] , cnt , vis[N];
int num[N] , len , ans[N];
struct query
{
	int l , r;
	int Lca , id;
} b[M];

void dfs(int x , int lst)
{
	for(int k = 1 ; k < K ; k++)
		dp[x][k] = dp[dp[x][k - 1]][k - 1];
	fir[x] = ++idx; dfn[idx] = x;
	for(int i = h[x] , v ; v = to[i] , i ; i = nxt[i])
	{
		if(v == lst) continue;
		dep[v] = dep[x] + 1;
		dp[v][0] = x;
		dfs(v , x);
	}
	las[x] = ++idx; dfn[idx] = x;
}
//inline int read()
//{
//	char Orz; int ret = 0;
//	while(sto) Orz;
//	while(StmAKioi)
//	{
//		ret = ret * 10 + c - '0';
//		Orz;
//	}
//	yzhyyds;
//}
inline int read()
{
	char c = getchar() ; int ret = 0;
	while(c < '0' || c > '9') c = getchar();
	while(c >= '0' && c <= '9')
	{
		ret = ret * 10 + c - '0';
		c = getchar();
	}return ret;
}
inline void add(int a , int b)
{
	to[++tot] = b ; nxt[tot] = h[a] ; h[a] = tot;
}
inline int lca(int x , int y)
{
	if(dep[x] < dep[y]) swap(x , y);
	for(int k = K - 1 ; k >= 0 ; k--)
		if(dep[dp[x][k]] >= dep[y]) x = dp[x][k];
	if(x == y) return x;
	for(int k = K - 1 ; k >= 0 ; k--)
	{
		if(dp[x][k] == dp[y][k]) continue;
		x = dp[x][k] , y = dp[y][k];
	}
	return dp[x][0];
}
inline int find(int x)
{
	int l = 1 , r = len , mid , best;
	while(l <= r)
	{
		mid = (l + r) >> 1;
		if(num[mid] >= x) best = mid , r = mid - 1;
		else l = mid + 1; 
	}return best;
}
inline bool cmp(query a , query b)
{
	return bel[a.l] ^ bel[b.l] ? bel[a.l] < bel[b.l] : ((bel[a.l] & 1) ? a.r < b.r : a.r > b.r);
}
inline void Add(int x)
{
	if(!col[a[x]]) cnt++;
	col[a[x]]++;
}
inline void Del(int x)
{
	if(col[a[x]] == 1) cnt--;
	col[a[x]]--;
}
inline void work(int x)
{
	vis[dfn[x]] ^= 1;
	if(vis[dfn[x]]) Add(x);
	else Del(x);
}
int main()
{
	n = read() ; m = read() ; LEN = sqrt(2 * n);
	for(int i = 1 ; i <= n ; i++) gg[i] = read() , num[i] = gg[i];
	sort(num + 1 , num + n + 1); len = unique(num + 1 , num + n + 1) - num - 1;
	for(int i = 1 ; i <= n ; i++) gg[i] = find(gg[i]);
	for(int i = 1 ; i <= 2 * n ; i++) bel[i] = (i - 1) / LEN;
	for(int i = 1 , a , b ; i < n ; i++)
	{
		a = read() ; b = read();
		add(a , b) , add(b , a);
	}
	dep[1] = 1;
	dfs(1 , 1);
	for(int i = 1 ; i <= n ; i++) a[fir[i]] = a[las[i]] = gg[i];
	for(int i = 1 ; i <= m ; i++)
	{
		b[i].l = read() ; b[i].r = read() ; 
		if(fir[b[i].l] > fir[b[i].r]) swap(b[i].l , b[i].r);
		b[i].Lca = lca(b[i].l , b[i].r); b[i].id = i;
		if(b[i].Lca == b[i].l) b[i].l = fir[b[i].l] , b[i].r = fir[b[i].r];
		else b[i].l = las[b[i].l] , b[i].r = fir[b[i].r];
	}
	sort(b + 1 , b + m + 1 , cmp);
	int l = 1 , r = 0;
	for(int i = 1 ; i <= m ; i++)
	{
		int ql = b[i].l , qr = b[i].r , LCA = b[i].Lca;
		if(dfn[ql] == dfn[qr])
		{
			ans[b[i].id] = 1;
			continue;
		}
		while(l < ql) work(l++);
		while(r < qr) work(++r);
		while(r > qr) work(r--);
		while(l > ql) work(--l);
		if(LCA == dfn[ql])
			ans[b[i].id] = cnt;
		else
			ans[b[i].id] = cnt + (!col[gg[LCA]] ? 1 : 0); 
	}
	for(int i = 1 ; i <= m ; i++) printf("%d\n" , ans[i]);
	return 0;
}

外送一道练习题P1997 faebdc 的烦恼

拓展:树上带修莫队

水一道黑题喔 (bushi

跟上面的没什么区别,改一改就能交

P4074 [WC2013]糖果公园

code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long 

using namespace std;
const int N = 300010 , M = 600010 , K = 23;
int h[N] , to[M] , nxt[M] , tot = 1;
int a[N] , n , mx , len1 , len2;
int fir[N] , las[N] , dfn[N] , idx , ttim;
int cnt[N] , m , LEN;
int dp[N][K] , dep[N] , bel[N] , vis[N];
int l = 1 , r = 0 , tt;
ll w[N] , v[N] , ans[N] , now;
struct query
{
	int l , r , t , id;
	int Lca;
} b[N];
struct work
{
	int x , val , t;
} c[N];

inline void add(int a , int b)
{
	to[++tot] = b ; nxt[tot] = h[a] ; h[a] = tot;
}
bool cmp(query a , query b)
{
	return (bel[a.l] ^ bel[b.l]) ? bel[a.l] < bel[b.l] : ((bel[a.r] ^ bel[b.r]) ? bel[a.r] < bel[b.r] : a.t < b.t); 
}
void dfs(int x , int lst)
{
	for(int k = 1 ; k < K ; k++) dp[x][k] = dp[dp[x][k - 1]][k - 1];
	fir[x] = ++idx ; dfn[idx] = x;
	for(int i = h[x] , v ; v = to[i] , i ; i = nxt[i])
	{
		if(v == lst) continue;
		dep[v] = dep[x] + 1 ; dp[v][0] = x;
		dfs(v , x);
	}
	las[x] = ++idx ; dfn[idx] = x;
}
inline int lca(int x , int y)
{
	if(dep[x] < dep[y]) swap(x , y);
	for(int k = K - 1 ; k >= 0 ; k--) if(dep[dp[x][k]] >= dep[y]) x = dp[x][k];
	if(x == y) return x;
	for(int k = K - 1 ; k >= 0 ; k--) if(dp[x][k] != dp[y][k]) x = dp[x][k] , y = dp[y][k];
	return dp[x][0];
}
inline void work(int x)
{
	vis[x] = 1 - vis[x];
	if(vis[x])
	{
		cnt[a[x]]++ ; 
		now += v[a[x]] * w[cnt[a[x]]];
	}
	else
	{
		now -= v[a[x]] * w[cnt[a[x]]];
		cnt[a[x]]--;
	}
}
inline void twork(int kk)
{
	int gg = c[kk].x;
	if(l <= fir[gg] && fir[gg] <= r) work(gg);
	if(l <= las[gg] && las[gg] <= r) work(gg);
	swap(c[kk].val , a[gg]);
	if(l <= fir[gg] && fir[gg] <= r) work(gg);
	if(l <= las[gg] && las[gg] <= r) work(gg);
}
inline ll read()
{
	char c = getchar() ; ll ret = 0;
	while(c < '0' || c > '9') c = getchar();
	while(c >= '0' && c <= '9')
	{
		ret = ret * 10 + c - '0';
		c = getchar();
	}
	return ret;
}
int main()
{
	n = read() ; mx = read() ; m = read();
	LEN = sqrt(2 * n);
	for(int i = 1 ; i <= mx ; i++) v[i] = read();
	for(int i = 1 ; i <= n ; i++) w[i] = read();
	for(int i = 1 ; i <= 2 * n ; i++) bel[i] = (i - 1) / LEN + 1;
	for(int i = 1 ; i < n ; i++)
	{
		ll a , b;
		a = read() ; b = read();
		add(a , b) ; add(b , a); 
	}
	dep[1] = 1;
	dfs(1 , 1);
	for(int i = 1 ; i <= n ; i++) a[i] = read();
	for(int i = 1 ; i <= m ; i++)
	{
		ll opt , x , y;
		opt = read() ; x = read() ; y = read();
		if(opt == 0)
		{
			ttim++;
			c[ttim].t = ttim; c[ttim].x = x , c[ttim].val = y;
		}
		else
		{
			b[++len1].t = ttim;
			b[len1].id = len1;
			if(fir[x] > fir[y]) swap(x , y);
			int LCA = lca(x , y);
			if(LCA == x) b[len1].l = fir[x] , b[len1].r = fir[y] , b[len1].Lca = 0;
			else b[len1].l = las[x] , b[len1].r = fir[y] , b[len1].Lca = LCA;
		}
	}
	sort(b + 1 , b + len1 + 1 , cmp);
	for(int i = 1 ; i <= len1 ; i++)
	{
		int ql = b[i].l , qr = b[i].r , qt = b[i].t , LCA = b[i].Lca;
		while(tt < qt) 
			twork(++tt);
		while(tt > qt) 
			twork(tt--);
		while(l < ql) work(dfn[l++]);
		while(l > ql) work(dfn[--l]);
		while(r < qr) work(dfn[++r]);
		while(r > qr) work(dfn[r--]);
		if(LCA) work(LCA);
		ans[b[i].id] = now;
		if(LCA) work(LCA);
	}
	for(int i = 1 ; i <= len1 ; i++) printf("%lld\n" , ans[i]);
	return 0;
}

第十四分块(前体

莫队二次离线

基于差分思想,将本来时间复杂度挺大的转移拆掉

具体的…慢慢理解

code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<tuple>
#define ll long long
using namespace std;

const int N = 200010;
int bel[N] , a[N];
int LEN , n , m , k;

ll t[N] , pref[N];
ll ans[N];

struct query
{
	int l , r , id ;
	ll ans;
} b[N];


vector<tuple<int , int , int> > v[N];
vector<int> buc;

inline bool cmp(query a , query b)
{
	return (bel[a.l] == bel[b.l]) ? a.r < b.r : bel[a.l] < bel[b.l];
}

inline int check(int x)
{
	int ret = 0;
	while(x)
	{
		ret++ ; x -= (x & (-x));
	}
	return ret;
}


int main()
{
	scanf("%d%d%d" , &n , &m , &k);
	
	if(k > 14)
	{
		for(int i = 1 ; i <= m ; i++) printf("0\n");
		return 0;
	}
	
	for(int i = 1 ; i <= n ; i++) scanf("%d" , &a[i]);
	for(int i = 1 ; i <= m ; i++) scanf("%d%d" , &b[i].l , &b[i].r) , b[i].id = i , b[i].ans = 0;
	LEN = sqrt(n);
	for(int i = 1 ; i <= n ; i++) bel[i] = (i - 1) / LEN + 1;
	sort(b + 1 , b + m + 1 , cmp);
	
	for(int i = 0 , inf = (1 << 14) ; i < inf ; i++)
		if(check(i) == k) buc.push_back(i);
	for(int i = 1 ; i <= n ; i++)
	{
		for(auto x : buc) ++t[a[i] ^ x];
		pref[i] = t[a[i + 1]];
	}
	
	memset(t , 0 , sizeof t);
	
	int l = 1 , r = 0;
	for(int i = 1 ; i <= m ; i++)
	{
		int ql = b[i].l , qr = b[i].r;
		
		if(l < ql) v[r].emplace_back(l , ql - 1 , -i);
		while(l < ql) b[i].ans += pref[l - 1] , l++;
		
		if(l > ql) v[r].emplace_back(ql , l - 1 , i);
		while(l > ql) b[i].ans -= pref[l - 2] , l--;
		
		if(r < qr) v[l - 1].emplace_back(r + 1 , qr , -i);
		while(r < qr) b[i].ans += pref[r] , ++r;
		
		if(r > qr) v[l - 1].emplace_back(qr + 1 , r , i);
		while(r > qr) b[i].ans -= pref[r - 1] , r--;
		
	}
	
	for(int i = 1 , id , l , r ; i <= n ; i++)
	{
		for(auto x : buc) ++t[a[i] ^ x];
		for(const auto& x : v[i])
		{
			tie(l , r , id) = x;
			for(int j = l , tmp = 0 ; j <= r ; j++)
			{
				tmp = t[a[j]];
				if(j <= i && k == 0) --tmp;
				if(id < 0) b[-id].ans -= tmp;
				else b[id].ans += tmp;
			}
		}
	}
	
	for(int i = 1 ; i <= m ; i++) b[i].ans += b[i - 1].ans;
	for(int i = 1 ; i <= m ; i++) ans[b[i].id] = b[i].ans;
	for(int i = 1 ; i <= m ; i++) printf("%lld\n" , ans[i]);
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值