复杂度
将一个区间询问变成了插入删除操作。分成 n \sqrt n n 块之后,遍历每个操作,可以发现块内 l l l 的排序时任意的,最多跑 n \sqrt n n,一共 n 个询问,复杂度为 n n n\sqrt n nn , r r r 是递增的最多跑 O ( n ) O(n) O(n) 一共 n \sqrt n n 块,复杂度也是 n n n\sqrt n nn。
P1972 [SDOI2009]HH的项链
链接:https://www.luogu.com.cn/problem/P1972
题意:给定一个序列 n ,给定 m 个询问,问区间内不同的数字个数。
思路:正解是树状数组,莫队复杂度为
O
(
n
n
)
O(n\sqrt n)
O(nn),只能跑 5e5的数据量
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e6+10;
int n,m,a[maxn],ans[maxn];
int block[maxn],B;
struct Opt
{
int l,r,aid;
bool operator<(const Opt & b) const
{
if(block[l]!=block[b.l]) return l<b.l;
return r<b.r;
}
}opt[maxn];
int nowans,cnt[maxn];
void update(int id,int f)
{
int p=a[id];
cnt[p]+=f;
if(f==1&&cnt[p]==1) nowans++;
if(f==-1&&cnt[p]==0) nowans--;
}
int main()
{
scanf("%d",&n);
B=sqrt(n);
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
block[i]=i/B;
}
scanf("%d",&m);
for(int i=1;i<=m;++i)
scanf("%d%d",&opt[i].l,&opt[i].r),opt[i].aid=i;
sort(opt+1,opt+1+m);
int l=1,r=0;
for(int i=1;i<=m;++i)
{
while(l<opt[i].l) update(l++,-1);
while(l>opt[i].l) update(--l,+1);
while(r<opt[i].r) update(++r,+1);
while(r>opt[i].r) update(r--,-1);
ans[opt[i].aid]=nowans;
}
for(int i=1;i<=m;++i)
printf("%d\n",ans[i]);
return 0;
}
P1494 [国家集训队]小Z的袜子
题意:给定一个序列 n ,给定 m 个区间询问,问在区间内取两个相同的数的概率
思路:可以得到答案为: ∑ C c n t i 2 C l e n 2 \frac {\sum C_{cnt_i}^2} {C_{len}^2} Clen2∑Ccnti2 ,发现修改数量可以O(1)维护,那么维护 ∑ C c n t i 2 {\sum C_{cnt_i}^2} ∑Ccnti2 这个值就可以了
链接:https://www.luogu.com.cn/problem/P1494
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e6+10;
int n,m,a[maxn];
ll ans[maxn][2];
int block[maxn],B;
struct Opt
{
int l,r,aid;
bool operator<(const Opt & b) const
{
if(block[l]!=block[b.l]) return l<b.l;
return r<b.r;
}
}opt[maxn];
ll nowans,cnt[maxn];
void update(int p,int f)
{
int x=a[p];
nowans-=cnt[x]*(cnt[x]-1);
cnt[x]+=f;
nowans+=cnt[x]*(cnt[x]-1);
}
int main()
{
scanf("%d%d",&n,&m);
B=sqrt(n);
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
block[i]=i/B;
}
for(int i=1;i<=m;++i)
scanf("%d%d",&opt[i].l,&opt[i].r),opt[i].aid=i;
sort(opt+1,opt+1+m);
int l=1,r=0;
for(int i=1;i<=m;++i)
{
while(l<opt[i].l) update(l++,-1);
while(l>opt[i].l) update(--l,+1);
while(r<opt[i].r) update(++r,+1);
while(r>opt[i].r) update(r--,-1);
int p=opt[i].aid;
if(l==r)
{
ans[p][0]=0;
ans[p][1]=1;
continue;
}
int len=r-l+1;
ll d=__gcd(nowans,1ll*len*(len-1));
ans[p][0]=nowans/d;
ans[p][1]=1ll*len*(len-1)/d;
}
for(int i=1;i<=m;++i)
printf("%lld/%lld\n",ans[i][0],ans[i][1]);
return 0;
}
P3709 大爷的字符串题
题意:给定一个序列 n ,给定 m 个区间询问,求区间众数
思路:新增一个 num 数组记录出现次数为 i 的数量
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+10;
int n,m,a[maxn],b[maxn];
int ans[maxn];
int block[maxn],B;
struct Opt
{
int l,r,aid;
bool operator<(const Opt & b) const
{
if(block[l]!=block[b.l]) return block[l]<block[b.l];
return r<b.r;
}
}opt[maxn];
int nowans,cnt[maxn],num[maxn];
void update(int p,int f)
{
int x=a[p];
if(f==1)
{
int y=cnt[x];
num[y]--;
num[y+1]++;
if(nowans==y) nowans++;
cnt[x]++;
}
else if(f==-1)
{
int y=cnt[x];
num[y]--;
num[y-1]++;
if(num[y]==0&&nowans==y) nowans--;
cnt[x]--;
}
}
int main()
{
scanf("%d%d",&n,&m);
B=sqrt(n);
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
b[i]=a[i];
block[i]=i/B;
}
sort(b+1,b+1+n);
int tot=unique(b+1,b+1+n)-b-1;
for(int i=1;i<=n;++i)
a[i]=lower_bound(b+1,b+1+tot,a[i])-b;
for(int i=1;i<=m;++i)
scanf("%d%d",&opt[i].l,&opt[i].r),opt[i].aid=i;
sort(opt+1,opt+1+m);
int l=1,r=0;
for(int i=1;i<=m;++i)
{
while(l<opt[i].l) update(l++,-1);
while(l>opt[i].l) update(--l,+1);
while(r<opt[i].r) update(++r,+1);
while(r>opt[i].r) update(r--,-1);
int p=opt[i].aid;
ans[p]=-nowans;
}
for(int i=1;i<=m;++i)
printf("%d\n",ans[i]);
return 0;
}
P4462 [CQOI2018]异或序列
链接:https://www.luogu.com.cn/problem/P4462
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+10;
int n,m,k,a[maxn];
int ans[maxn];
int block[maxn],B;
struct Opt
{
int l,r,aid;
bool operator<(const Opt & b) const
{
if(block[l]!=block[b.l]) return block[l]<block[b.l];
return r<b.r;
}
}opt[maxn];
int nowans,cnt[maxn];
void update(int x,int f)
{
if(f==1)
{
nowans+=cnt[a[x]^k];
cnt[a[x]]++;
}
else
{
cnt[a[x]]--;
nowans-=cnt[a[x]^k];
}
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
B=sqrt(n);
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
a[i]^=a[i-1];
block[i]=i/B;
}
for(int i=1;i<=m;++i)
scanf("%d%d",&opt[i].l,&opt[i].r),opt[i].aid=i;
sort(opt+1,opt+1+m);
int l=1,r=0;
for(int i=1;i<=m;++i)
{
opt[i].l--;
while(l<opt[i].l) update(l++,-1);
while(l>opt[i].l) update(--l,+1);
while(r<opt[i].r) update(++r,+1);
while(r>opt[i].r) update(r--,-1);
int p=opt[i].aid;
ans[p]=nowans;
}
for(int i=1;i<=m;++i)
printf("%d\n",ans[i]);
return 0;
}