您的位置:首页 > 其它

P1972 [SDOI2009] HH的项链(莫队)

2017-03-29 14:41 323 查看
https://www.luogu.org/problem/show?pid=1972#sub

时间复杂度证明:

右端点移动:

首先我们考虑一个块里面的转移情况

由于一个块里面的询问都按右端点排序

所以我们右端点在一个块里面最多移动n次

有 O(n√)O(n)个块,那么同一个块内的右端点移动最多就是O(nn√)O(nn)

然后考虑从一个块到另一个块导致的右端点变化

最坏情况,右端点由n到1,那么移动n次

有 O(n√)O(n)个块

那么从一个块到另一个块的事件只会发生O(n√)O(n)次……

所以这种右端点移动的次数也是O(nn√)O(nn)次

没有别的事件导致右端点移动了

左端点移动:

同一个块里面,由于左端点都在一个长度为O(n√)O(n)的区间里面

所以在同一块里面移动一次,左端点最多变化O(n√)O(n)

总共有n个询问……

所以同一块里面的移动最多n次

那么同一个块里面的左端点变化最多是O(nn√)O(nn)的

考虑跨越块

每由第i个块到第i+1个块,左端点最坏加上O(n√)O(n)

总共能加上O(n√)O(n)次

所以跨越块导致的左端点移动是O(n)O(n)的

综上,分块做法是O(n∗n√)O(n∗n)。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
struct H{
int l,r,id,qv;
}Q[200009];
int a[50009],n,m,limit,sum=0;
int num[1000009],ans[200009];
int my_comp(const H&a,const H&b)
{
if(a.qv<b.qv) return 1;//根据左端点分块
if(a.qv>b.qv) return 0;
if(a.r<b.r) return 1;//在块内按照r升序
return 0;
}
void add(int x)
{
num[x]++;
if(num[x]==1) sum++;//
}
void del(int x)
{
num[x]--;
if(num[x]==0) sum--;//
}
int main()
{
scanf("%d",&n);limit=(int)(sqrt((double)(n)+0.5));
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&Q[i].l,&Q[i].r);
Q[i].qv=Q[i].l/limit;Q[i].id=i;//id 为第几个询问,离线输出
}

sort(Q+1,Q+m+1,my_comp);

int r=0,l=0;
for(int i=1;i<=m;i++)
{
while(r<Q[i].r){
r++;
add(a[r]);
}
while(r>Q[i].r){
r--;
del(a[r+1]);
}
while(l<Q[i].l){
l++;
del(a[l-1]);
}
while(l>Q[i].l){
l--;
add(a[l]);
}
ans[Q[i].id]=sum;
}
for(int i=1;i<=m;i++)
{
printf("%d\n",ans[i]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: