分块的思想大家应该都不陌生,作为线段树或者树状数组的简化版本,分块主要用于解决......之类的区间操作。
#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;
}
以上是分块的一些基础操作,接下来就可以去搞莫队了
莫队是一个神奇的算法,先来一道例题: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;
}