初识莫队算法,等做的题目多了再汇总一下。
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<map>
#include<string.h>
#include<cmath>
using namespace std;
const int N = 100005;
int T,n,q,l,r,sqr,cur;
int a[N],ans[N],num[N];
/*
* 初识莫队算法,比赛的时候没写出来http://acm.hdu.edu.cn/showproblem.php?pid=4638
* 后来看了网上的答案才理解着写出来的,有点乌龙的是忘了把数组清零
* 题意:给一个数组,多次询问区间,输出区间中的数字可以组成最少的连续序列的个数
* (题目设定每个数字具有唯一性,这是我转化过来的题意,感觉题目的说法有点怪)
* 思路:如果用莫队,那就是基本操作,保存询问,排序,实现add和sub,最后输出ans数组;
* 实现过程有些特别,见下面函数注释
*
* (以后再试试用树状数组来写)
*/
struct node
{
int l,r,i,b;
bool operator < (node x){
if(b==x.b)return r<x.r;
return b<x.b;
}//用分块降低复杂度,其实没有那么复杂,按照块为第一关键字,r为第二关键字去排序
//至于理解,可以想成减少r反向移动的次数,当然分块不是莫队最优的情况
}Q[N];
void sub(int i){
int t=a[i];
num[t]=0;
if(num[t-1]&&num[t+1]){
cur++;
} else if(!num[t-1]&&!num[t+1]) {
cur--;
}
}
void add(int i){
int t=a[i];
num[t]=1;
if(num[t-1]&&num[t+1]){
cur--;//如果这个数相邻的两个数字已经存在,这个数会链接两个序列
} else if(!num[t-1]&&!num[t+1]) {
cur++;//如果相邻的两个数字不存在,这个数会创建一个新的序列
}//如果一端一个数存在,那么就会加入到已有的序列
}
int main(){
scanf("%d",&T);
while(T--)
{
memset(num,0,sizeof(num));//无奈wa了好几次!
scanf("%d%d",&n,&q);
sqr=(int)round(sqrt(1.0*n));
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=q;i++)
{
scanf("%d%d",&l,&r);
Q[i]={l,r,i,l/sqr};
}
sort(Q+1,Q+1+q);//q和n傻傻分不清wa了一次,变量名最好起得区分度大一些
cur=0,l=1,r=1,add(1);
for(int i=1;i<=q;i++)
{
//注意这个顺序,先扩张再缩小
//否则会有r<l的情况,导致意外的元素计入
while(r<Q[i].r)add(++r);
while(l>Q[i].l)add(--l);
while(l<Q[i].l)sub(l++);
while(r>Q[i].r)sub(r--);
ans[Q[i].i]=cur;
}
for(int i=1;i<=q;i++)
printf("%d\n",ans[i]);
}
return 0;
}
/*
21
5 2
3 1 2 5 4
1 5
2 4
5 2
3 1 2 5 4
1 5
2 4
*/