分块+莫队

分块的思想大家应该都不陌生,作为线段树或者树状数组的简化版本,分块主要用于解决......之类的区间操作。

分块1<区间加法+单点查询>

#include<bits/stdc++.h>
using namespace std;
int n,a[50010];
int q[50010],tag[50010],length;
void change(int l,int r,int x)
{
	for(int i=l;i<=min(r,q[l]*length);i++) a[i]+=x;
	if(q[l]!=q[r])
		for(int i=(q[r]-1)*length+1;i<=r;i++) a[i]+=x;
	for(int i=q[l]+1;i<=q[r]-1;i++) tag[i]+=x;
}
int main()
{
	scanf("%d",&n);
	length=sqrt(n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		q[i]=(i-1)/length+1;//谁在哪个块里 
	}
	for(int i=1;i<=n;i++)
	{
		int op,l,r,c;
		scanf("%d%d%d%d",&op,&l,&r,&c);
		if(op==0) change(l,r,c);
		else printf("%d\n",a[r]+tag[q[r]]);
	}
	return 0;
}
/*
4
1 2 2 3
0 1 3 1
1 0 1 0
0 1 2 2
1 0 2 0
*/

区间加法+区间比某个数小的数有多少个

#include<bits/stdc++.h>
using namespace std;
int n,num,sq;
int a[50010],b[50010],q[50010],tag[50010];
void order(int l,int r)
{
	for(int i=l;i<=r;i++) b[i]=a[i];
	sort(b+l,b+r+1);
}
void change(int l,int r,int c)
{
	for(int i=l;i<=min(r,sq*q[l]);i++) a[i]+=c;
	order((q[l]-1)*sq+1,min(n,q[l]*sq));
	if(q[l]!=q[r]);
	{
		for(int i=(q[r]-1)*sq+1;i<=r;i++) a[i]+=c;
		order((q[r]-1)*sq+1,min(n,q[r]*sq));
	}
	for(int i=q[l]+1;i<=q[r]-1;i++) tag[i]+=c;
}
int ask(int l,int r,int c)
{
	int ans=0;
	for(int i=l;i<=min(r,q[l]*sq);i++) if(a[i]+tag[q[l]]<c) ans++;
	if(q[l]!=q[r])
		for(int i=(q[r]-1)*sq+1;i<=r;i++)
			if(a[i]+tag[q[r]]<c) ans++;
	for(int i=q[l]+1;i<=q[r]-1;i++)
	{
		int pos=lower_bound(b+sq*(i-1)+1,b+sq*i+1,c-tag[i])-(b+(i-1)*sq+1);
		ans+=pos;
	}
	return ans;
}
int main()
{
	scanf("%d",&n);
	sq=sqrt(n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		q[i]=(i-1)/sq+1;
	}
	num=n/sq;
	if(n%sq) num++;
	for(int i=1;i<=num;i++) order((i-1)*sq+1,min(n,sq*i));
	for(int i=1;i<=n;i++)
	{
		int op,l,r,c;
		scanf("%d%d%d%d",&op,&l,&r,&c);
		if(op==0) change(l,r,c);
		else printf("%d\n",ask(l,r,c*c));
	}
	return 0;
}
/*
4
1 2 2 3
0 1 3 1
1 1 3 2
1 1 4 1
1 2 3 2
*/

再写的时候有一点坑,就是再求和的时候习惯于long long,但是因为经常出现要当下标的指针,所以就总是把long long形当作下标了,本地会报错。

区间加法+区间求和

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=100010;
int n,t,ans;
int a[N],pos[N],add[N],L[N],R[N],sum[N];
void change(int l,int r,int c)
{
	int p=pos[l],q=pos[r];
	if(p==q)
	{
		for(int i=l; i<=r; i++)a[i]+=c;
		sum[p]+=c*(r-l+1);
		return ;
	}
	for(int i=p+1; i<=q-1; i++)
	{
		add[i]+=c;
		sum[i]+=c*(R[i]-L[i]+1);
	}
	change(l,R[p],c);
	change(L[q],r,c);
	return ;
}
int ask(int l,int r)
{
	int ans=0;
	int p=pos[l],q=pos[r];
	if(p==q)
	{
		for(int i=l; i<=r; i++) ans+=(a[i]+add[p]);
		return ans;
	}
	for(int i=p+1; i<=q-1; i++) ans+=sum[i]; 
	ans+=ask(l,R[p]);
	ans+=ask(L[q],r);
	return ans;
}
signed main()
{
	ios::sync_with_stdio(false);
	cin>>n;
	for(int i=1; i<=n; i++) cin>>a[i];
	t=sqrt(n);
	for(int i=1; i<=t; i++)
	{
		L[i]=(i-1)*t+1;
		R[i]=i*t;
	}
	if(R[t]<n)
	{
		t++;
		L[t]=R[t-1]+1;
		R[t]=n;
	}
	for(int i=1; i<=t; i++)
	{
		for(int j=L[i]; j<=R[i]; j++)
		{
			pos[j]=i;
			sum[i]+=a[j];
		}
	}
	for(int i=1; i<=n; i++)
	{
		int op,l,r,c;
		cin>>op>>l>>r>>c;
		if(op==0) change(l,r,c);
		else cout<<ask(l,r)%(c+1)<<endl;
	}
	return 0;
}

以上是分块的一些基础操作,接下来就可以去搞莫队了

来自莫队算法 --算法竞赛专题解析(26)

莫队是一个神奇的算法,先来一道例题:P1972 HH的项链

题目大意就是说给一串数,问某区间内的某个数出现了多少次

#include<bits/stdc++.h>
using namespace std;
int n,m,a[1000010],sq,cnt[1000010],ans[1000010];
struct ask{int l,r,id;}q[1000010];
bool cmp(ask aa,ask bb)
{
    int a=aa.l/sq;
    int b=bb.l/sq;
    if(a!=b) return a<b;
    return aa.r<bb.r;
}
void del(int aa,int &x)
{
    cnt[aa]--;
    if(cnt[aa]==0) x--;
}
void add(int aa,int &x)
{
    if(cnt[aa]==0) x++;
    cnt[aa]++;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    scanf("%d",&m);
    sq=sqrt((double)n*n/m);
    for(int t=0;t<m;t++)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        q[t].id=t,q[t].l=l,q[t].r=r;
    }
    sort(q,q+m,cmp);
    for(int i=0,j=1,k=0,res=0;k<m;k++)
    {
        int x=q[k].id;
        int lll=q[k].l;
        int rrr=q[k].r;
        while(i<rrr) add(a[++i],res);
        while(i>rrr) del(a[i--],res);
        while(j<lll) del(a[j++],res);
        while(j>lll) add(a[--j],res);
        ans[x]=res;
    }
    for(int t=0;t<m;t++) printf("%d\n",ans[t]);
    return 0;
}

非正解是真的会TLE得很惨qwq

小Z的袜子(终于有莫队正解的题了   几乎没什么区别

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll n,m,len,a[50010],sum[50010],sq[50010],ans;
struct node{int l,r,id;ll res,x;}q[50010];
ll gcd(ll aa,ll bb)
{
    if(bb==0) return aa;
    return gcd(bb,aa%bb);
}
bool cmp1(node aa,node bb)
{
    if(sq[aa.l]==sq[bb.l]) return aa.r<bb.r;
    else return aa.l<bb.l;
}
bool cmp2(node aa,node bb)
{ return aa.id<bb.id; }
void add(ll aa)
{
    ans+=sum[a[aa]];
    sum[a[aa]]++;
}
void del(ll bb)
{
    sum[a[bb]]--;
    ans-=sum[a[bb]];
}
int main()
{
    scanf("%lld%lld",&n,&m);
    len=(int)sqrt(n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        sq[i]=i/len;
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%lld%lld",&q[i].l,&q[i].r);
        q[i].id=i;
    }
    sort(q+1,q+m+1,cmp1);
    ll l=1,r=ans=0;
    for(int i=1;i<=m;i++)
    {
        while(l<q[i].l) del(l++);
        while(l>q[i].l) add(--l);
        while(r<q[i].r) add(++r);
        while(r>q[i].r) del(r--);
        q[i].res=ans;
        q[i].x=(r-l+1)*(r-l)/2;
        if(q[i].res==0)
        {
            q[i].x=1;
            continue;
        }
        ll tmp=gcd(q[i].res,q[i].x);
        q[i].res/=tmp;
        q[i].x/=tmp;
    }
    sort(q+1,q+m+1,cmp2);
    for(int i=1;i<=m;i++) printf("%lld/%lld\n",q[i].res,q[i].x);
    return 0;
}

 

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
莫队算法是一种基于分块算法,用于解决一些静态区间查询问题,时间复杂度为 $O(n\sqrt{n})$。以下是一个基于Python的莫队算法的示例代码: ```python import math # 定义块的大小 BLOCK_SIZE = 0 # 初始化块的大小 def init_block_size(n): global BLOCK_SIZE BLOCK_SIZE = int(math.sqrt(n)) # 定义查询操作 def query(left, right): pass # 在这里写查询操作的代码 # 定义添加操作 def add(x): pass # 在这里写添加操作的代码 # 定义删除操作 def remove(x): pass # 在这里写删除操作的代码 # 定义莫队算法 def mo_algorithm(n, q, queries): init_block_size(n) queries.sort(key=lambda x: (x[0] // BLOCK_SIZE, x[1])) left, right = 0, -1 for query in queries: while left > query[0]: left -= 1 add(left) while right < query[1]: right += 1 add(right) while left < query[0]: remove(left) left += 1 while right > query[1]: remove(right) right -= 1 query(query[0], query[1]) ``` 在这段代码中,我们首先定义了一个全局变量 `BLOCK_SIZE`,用于表示块的大小。接着,我们定义了三个操作函数 `query()`、`add()` 和 `remove()`,分别用于查询、添加和删除元素。在 `mo_algorithm()` 函数中,我们首先调用 `init_block_size()` 函数初始化块的大小,然后将查询操作按照块的大小和右端点排序,接着使用双指针维护当前查询区间的左右端点,每次移动指针时调用 `add()` 和 `remove()` 函数更新块的状态,最后调用 `query()` 函数进行查询操作。 请注意,这段代码只是一个示例,具体的 `query()`、`add()` 和 `remove()` 函数的实现取决于具体的问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiyuping24

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值