莫队 &带修莫队

莫队算法使用分块的思想,可以解决一类离线区间询问问题。 对于序列上的区间询问问题,如果从 [l,r] 的答案能够 O(1) 扩展到
[l−1,r],[l+1,r],[l,r+1],[l,r−1] 的答案,那么可以在 O(n√​n​​​) 的复杂度内求出所有询问的答案。
将整个区间分为√​n个块,然后进行排序。L需要在对应的块中,而在上面的前提下R要递增。

P1972 [SDOI2009]HH的项链
这是一道可以用莫队写的题,最后两个点需要你吸一口氧气才能过`#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 1e6+4;
struct query {
int l, r,num;
}p[maxn];
int sum[maxn] ={0} , belong[maxn];
int cmp(query a, query b) {
return (belong[a.l] ^ belong[b.l]) ? belong[a.l] < belong[b.l] : ((belong[a.l] & 1) ? a.r < b.r : a.r > b.r);
}
int a[maxn],ans[maxn];
int main()
{
int n, m ;
scanf("%d",&n);
int size = sqrt(n);
int bnum = ceil((double)n/size);

for(int i=1;i<=bnum;i++)
	for(int j =(i-1) * size + 1 ;j <= i * size ;j++)
	belong[j] = i; 
	
for(int i=1;i<=n;i++)
	scanf("%d",&a[i]);

scanf("%d",&m);
for(int i=1;i<=m;i++)
{
	scanf("%d%d",&p[i].l,&p[i].r);
	p[i].num = i ;  
}
sort(p+1,p+m+1,cmp);
int l = 1,r =0 ,now =0;
for(int i=1;i<=m;i++)
{
	int ql = p[i].l, qr = p[i].r;
    while(l<ql) now -= !--sum[a[l++]];
    while(l>ql) now += !sum[a[--l]]++;
    while(r<qr) now += !sum[a[++r]]++;
    while(r>qr) now -= !--sum[a[r--]];
    ans[p[i].num] = now;
} 
for(int i=1;i<=m;i++)
{
	printf("%d\n",ans[i] );
}

}`


杭电多校第三场Game
题意就是一个数组两种操作,交换两个相邻点(修改一个异或前缀和)询问子区间的种相同元素的个数。明显是一个单点修改的待、带修改的莫队,就是再普通的莫队上再加上一维时间(所以又有人叫三维莫队),想学出门右转洛谷日报或者百度。
代码如下

#include <bits/stdc++.h>
#define ll long long 
using namespace std;
const int N = 1e5 + 10;
const int M = 1e5 + 10;
const int K = (1 << 21) + 5;
int a[N],b[N]={0},p[N];
ll  ans[N],cnt[K],sum=0;
int block;
struct node {int st,ed,t,id;}q[M];
	bool operator < (node a, node b) {
		if (a.st / block != b.st / block) return a.st < b.st;
		if (a.ed / block != b.ed / block) return a.ed < b.ed;
		return a.t < b.t;
	}
void add(int pos){sum+=cnt[b[pos]]++;}
void sub(int pos){sum-=--cnt[b[pos]];}
void upd(int i,int t)
{
	if(q[i].st <= p[t] &&  p[t]<= q[i].ed)sub(p[t]);
	b[p[t]]^=a[p[t]];
	swap(a[p[t]],a[p[t]+1]);
	b[p[t]]^=a[p[t]];
	if(q[i].st <=p[t]&&p[t]<=q[i].ed)add(p[t]);
}
int main()
{
	int n,m;
	while(~scanf("%d%d",&n,&m))
	{
		memset(cnt,0,sizeof cnt);
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			b[i] = b[i-1]^a[i];
		}
		int qcnt=0,kcnt=0,op,l,r,pla;
		for(int i=1;i<=m;i++)
		{
			scanf("%d",&op);
			if(op==1)
			{
				scanf("%d%d",&l,&r);
				q[++qcnt] = node{l-1,r,kcnt,qcnt};
			}
			else {
				scanf("%d",&pla);
				p[++kcnt] = pla;
			}
		}
			block = max(10, (int)pow(n, 2. / 3));
			sort(q+1,q+1+qcnt);
			sum = 0;
			int st=0,ed = -1,t= 0 ;
			for(int i=1;i<=qcnt;i++)
			{
				while(st<q[i].st)sub(st++);
				while(st>q[i].st)add(--st);
				while(ed<q[i].ed)add(++ed);
				while(ed>q[i].ed)sub(ed--); 
				while(t<q[i].t) upd(i,++t);
				while(t>q[i].t) upd(i,t--);
				ans[q[i].id] = 1ll*(ed-st) * (ed-st+1)/2 - sum;
			}
			for(int i=1;i<=qcnt;i++)
			printf("%lld\n",ans[i]);
	}
	return 0;
	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值