文章标题 HDU 4638 : Group (莫队算法)

Group

题目链接
题意:有n个数的排列,然后有m个询问,然后对于连续的一段数,这些数可以作为一个分组,然后对于每次询问给定一个区间[ L,R ] ,问这个区间内有多少个组。
分析: 可以用莫队算法,对于当前已知区间[ L,R ] 的答案,我们可以O(1)去得到(L+1,R),(L-1,R),(L,R+1),(L,R-1)的答案,可以用对于当前的点 u, 如果加上他,如果flag[u-1],flag[u+1]已经在区间中,说明区间(答案)可以减1,如果flag[u-1]和flag[u+1]都没有被标记,说明加上他区间(答案)得加1,存在一个标记不改变答案;对于减去当前点 u ,则相反。
注意:就是有可能就是两个区间是不相交的,这个得特殊判断
代码:

#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<vector>
#include<math.h>
#include<map>
#include<queue> 
#include<algorithm>
using namespace std;
const int inf = 0x3f3f3f3f;
typedef pair<int,int> pii;

const int maxn=1e5+10;
int R,L,ans;
int res[maxn];

struct node {
    int l,r,id;
}p[maxn]; 
int pos[maxn];//表示在第几个块 
bool cmp(node a,node b){//排序 
    if (pos[a.l]==pos[b.l])return pos[a.r]<pos[b.r];
    return pos[a.l]<pos[b.l];
}
int a[maxn],n,m;
int flag[maxn];

void add(int x){//加入一个数 
    if (flag[a[x]])return;
    if (flag[a[x]-1]&&flag[a[x]+1])ans--;//区间减一 
    else if (flag[a[x]-1]==0&&flag[a[x]+1]==0)ans++;//区间加一 
    flag[a[x]]=1;//加上标记 
}
void del(int x){
    if (flag[a[x]]==0)return;
    if (flag[a[x]-1]&&flag[a[x]+1])ans++;//区间加一 
    else if (flag[a[x]-1]==0&&flag[a[x]+1]==0)ans--;//区间减一 
    flag[a[x]]=0;//减去标记 
}
int main ()
{
    int T;
    scanf ("%d",&T);
    while (T--){
        scanf ("%d%d",&n,&m);
        memset (flag,0,sizeof (flag));
        int block=sqrt(n);
        for (int i=1;i<=n;i++){
            scanf ("%d",&a[i]);
            pos[i]=(i-1)/block+1;//得到快的位置 
        }
        for (int i=1;i<=m;i++){
            scanf ("%d%d",&p[i].l,&p[i].r);
            p[i].id=i;
        }
        sort(p+1,p+1+m,cmp);
        L=1;R=0;ans=0;
        for (int i=1;i<=m;i++){
            if (p[i].l>R||p[i].r<L){//区间不相交 
                while (L<=R){ 
                    flag[a[L++]]=0;
                }
                ans=0;
                L=p[i].l;
                R=p[i].l-1;
                while (R<p[i].r){
                    R++;
                    add(R);
                }
                res[p[i].id]=ans;
                continue;
            }
            while (L<p[i].l){
                del(L);
                L++;
            }
            while (L>p[i].l){
                L--;
                add(L);
            }
            while (R<p[i].r){
                R++;
                add(R);
            }
            while (R>p[i].r){
                del(R);
                R--;
            }
            res[p[i].id]=ans;
        }
        for (int i=1;i<=m;i++){
            printf ("%d\n",res[i]);
        }
    }

    return 0;
}
/*
1
5 2
3 1 2 5 4
1 2
4 5
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值