https://vjudge.net/problem/HDU-4638
题目大意:给出
n
n
n个人的编号,如果一些人的编号是连续的,那么可以把他们放到同一组里面,比如
1
、
2
、
3
1、2、3
1、2、3可以构成一个
3
3
3个人的组,
i
i
i个人的组可以产生
i
∗
i
i*i
i∗i的价值,现在有
m
m
m个询问,每个询问
(
l
,
r
)
(l,r)
(l,r),让你输出
[
l
,
r
]
[l,r]
[l,r]的这些人产生最大的价值时的分组数量。
思路:很明显,想要产生更大的价值,就要让编号连续的人放到同一组中,所以相当于查询 [ l , r ] [l,r] [l,r]内编号连续的段数。如果知道了 [ l , r ] [l,r] [l,r]的结果,就很容易计算出相邻区间的结果,所以就可以上莫队了。具体的修改操作看代码吧。
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
int n,m,res;
int a[maxn],pos[maxn],cnt[maxn],ans[maxn];
struct node
{
int l,r,id;
node(){}
node(int l,int r,int i):l(l),r(r),id(i){}
bool operator <(const node &a)const
{
if(pos[l]==pos[a.l])
return r<a.r;
return pos[l]<pos[a.l];
}
}q[maxn];
inline void add(int val)
{
++cnt[val];
if(cnt[val-1]&&cnt[val+1])//把两段不连续的连起来了
--res;
else if(!cnt[val-1]&&!cnt[val+1])//单独的一个编号
++res;
}
inline void sub(int val)
{
--cnt[val];
if(cnt[val-1]&&cnt[val+1])//把一段连续的分开了
++res;
else if(!cnt[val-1]&&!cnt[val+1])//消去了一段
--res;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
res=0;
scanf("%d%d",&n,&m);
int dis=sqrt(n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
pos[i]=(i-1)/dis+1;
}
for(int i=0;i<m;i++)
{
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id=i;
}
sort(q,q+m);
int l=1,r=0;
for(int i=0;i<m;i++)
{
while(l<q[i].l)
sub(a[l++]);
while(l>q[i].l)
add(a[--l]);
while(r<q[i].r)
add(a[++r]);
while(r>q[i].r)
sub(a[r--]);
ans[q[i].id]=res;
}
for(int i=0;i<m;i++)
printf("%d\n",ans[i]);
memset(cnt,0,sizeof(int)*(n+1));
}
return 0;
}