刷题记录luoguP1972 [SDOI2009]HH的项链
来源:互联网 发布:java ssh jar 编辑:程序博客网 时间:2024/04/26 20:36
这题是典型的区间处理问题
由于数据规模,我们可以采用离线操作:先将询问区间保存下来,对区间进行合理的排序,便于降低复杂度
然后采用合适的算法
(1)树状数组
我们可以给相应元素一个权值1,用树状数组计算sum,
但这题显然一般的计算是行不通的,
考虑到区间的有序性,我们以区间左端点排序,
然后依次处理,这样对于区间i+1左端点一定不会在区间i左端点之前
那么我们处理到区间i的时候,i左端点之前的都是废物,可以扔掉,反正后面又不会用到
具体怎么做呢?
设位置i的对应值是a[i],那么记录下a[i]出现的下个位置,不妨保存在next数组里面
举个栗子:
a 1 2 2 4 3 2
next 0 3 6 0 0 0
好的,我们已经处理的区间左端点是j,现在要处理k了
对于j到k之间的所有元素,统统扔掉,update(相应位置,-1)
然后一直next,直到大于等于k,update(相应位置,+1)。
举个栗子:
a 1 2 2 4 3 2
next 0 3 6 0 0 0
正准备处理4 5
[1,4)直接干掉
1next是0,直接update(1,-1)
2next是3,update(2,-1)
不行啊,3还是小于4 继续next 为6 update(3,-1) update(6,1)
同理继续搞就是了
这样就可以用树状数组求和了
这个方法主要是利用了离线的方法,通过合理调整顺序,以到达降低复杂度的效果。
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#include<algorithm>
#define pii pair<int,int>
#define MAXN 50005
#define MAXM 200005
#define MAXS 1000005
using namespace std;
struct Ask{
int L,R;
int num;
int ans;
Ask(){
L=R=num=ans=0;
}
};
int dat[MAXN];
int a[MAXN],b[MAXN];
int nxt[MAXN],lst[MAXS];
int fst[MAXN];
Ask p[MAXM];
int n,m;
bool comp1(const Ask &p1,const Ask &p2){
return (p1.L<p2.L);
}
bool comp2(const Ask &p1,const Ask &p2){
return (p1.num<p2.num);
}
void update(int k,int x){
while(k<=n){
dat[k]+=x;
k+=(k&-k);
}
}
int sum(int k){
int ret=0;
while(k>=1){
ret+=dat[k];
k-=(k&-k);
}
return ret;
}
int main()
{
// freopen("data.in","r",stdin);
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(lst[a[i]]){
int x=lst[a[i]];
nxt[x]=i;
}
else{
fst[i]=1;
}
lst[a[i]]=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,comp1);
for(int i=1;i<=n;i++){
if(fst[i]){
b[i]=1;
update(i,1);
}
}
int temp=1;
for(int i=1;i<=m;i++){
int L=p[i].L,R=p[i].R;
for(int j=temp;j<L;j++){
b[j]=0;
update(j,-1);
int cnt=j;
while(cnt<L&&cnt){
cnt=nxt[cnt];
}
if(cnt){
b[cnt]=1;
update(cnt,1);
}
}
p[i].ans=sum(R)-sum(L-1);
// printf("%d\n",ans);
temp=L;
}
sort(p+1,p+m+1,comp2);
for(int i=1;i<=m;i++){
printf("%d\n",p[i].ans);
}
return 0;
}
(2)莫队算法
莫队算法适用条件:已知[L,R]求出[L+/-1,R]或者是[L,R+/-1]很容易就可以啦
这题显然是很容易求出来的,我们定义一个数组b,记录[L,R]次数
当新加进去的元素或者新跑出来的元素Z,使得b[Z]变得非零或者变成零,那么ans++或者ans--即可
没学过的同学可以百度莫队算法
其实它的思想也是通过离线来调整区间顺序,使得效率最大化的
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#define MAXM 200005
#define MAXN 50005
#define MAXS 1000005
using namespace std;
struct Ask{
int L,R;
int id;
int ans;
};
Ask qu[MAXM];
int n,m;
int a[MAXN],pos[MAXN];
int len;
int b[MAXS];
bool comp1(const Ask &q1,const Ask &q2){
if(pos[q1.L]==pos[q2.L]){
return (q1.R<q2.R);
}
else{
return (pos[q1.L]<pos[q2.L]);
}
}
bool comp2(const Ask &q1,const Ask &q2){
return (q1.id<q2.id);
}
int main()
{
int n;
scanf("%d",&n);
len=sqrt(1.0*n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
pos[i]=i/len+1;
}
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&qu[i].L,&qu[i].R);
qu[i].id=i;
}
sort(qu+1,qu+m+1,comp1);
int ll=1,rr=0;
int ans=0;
for(int i=1;i<=m;i++){
int L=qu[i].L,R=qu[i].R;
if(rr<R){
for(int j=rr+1;j<=R;j++){
if(!b[a[j]]){
ans++;
}
b[a[j]]++;
}
}
else{
for(int j=rr;j>R;j--){
b[a[j]]--;
if(!b[a[j]]){
ans--;
}
}
}
if(ll<L){
for(int j=ll;j<L;j++){
b[a[j]]--;
if(!b[a[j]]){
ans--;
}
}
}
else{
for(int j=ll-1;j>=L;j--){
if(!b[a[j]]){
ans++;
}
b[a[j]]++;
}
}
qu[i].ans=ans;
ll=L; rr=R;
}
sort(qu+1,qu+m+1,comp2);
for(int i=1;i<=m;i++){
printf("%d\n",qu[i].ans);
}
return 0;
}
- 刷题记录luoguP1972 [SDOI2009]HH的项链
- luoguP1972 [SDOI2009]HH的项链(莫队)
- [SDOI2009]HH的项链
- 【SDOI2009】HH的项链
- [SDOI2009]HH的项链
- [BZOJ1878][SDOI2009]HH的项链
- [BZOJ1878] [SDOI2009]HH的项链
- 【SDOI2009】【BZOJ1878】HH的项链
- 【bzoj1878】[SDOI2009]HH的项链
- 1878: [SDOI2009]HH的项链
- 【bzoj1878】[SDOI2009]HH的项链
- (洛谷)[SDOI2009]HH的项链
- BZOJ1878: [SDOI2009]HH的项链
- 【bzoj1878】[SDOI2009]HH的项链
- bzoj1878 [SDOI2009]HH的项链
- luogu1972 [SDOI2009]HH的项链
- 1878: [SDOI2009]HH的项链
- bzoj1878 [SDOI2009]HH的项链
- SpringMVC与Struts2区别与比较总结
- java-se object
- python015 Python3 函数
- 运行shell脚本的三种方式
- 14个常用的javaScript正则表达式
- 刷题记录luoguP1972 [SDOI2009]HH的项链
- 最大公约数算法
- 宏定义中的反斜杠"\"和宏定义的细节说明
- STM32控制步进电机实现精确转动
- 静态代码块,构造代码块,构造方法的加载执行顺序研究
- C语言实现C++的封装继承与多态
- 手机ios安卓扫描车牌识别SDK
- 2017年6月,两个手游换皮项目,第六,七
- 第8,9个项目,手游开发,棋牌