hdu4638 莫队算法第一题

题目大意:在给定的区间里面找出sort后能分成连续的几段,
莫队算法:利用[L,R]区间的值去算出[L,R+1],[L,R-1],[L-1,R],[L+1,R]四个区间的值,适用于离线处理区间的问题,一般先把询问排序,顺序看题目要求,一般要分块处理,为了降低复杂度,让每次指针的移动次数都尽可能的少,然后暴力求出第一组解,再依次往下推。
针对本题,求出了[l,r]区间之后去推另一个区间的值,加入另一个区间是[l,r+1],就把r+1这个位置处理(update),原数组为a,把a[i+1]处理,利用vis记录当前区间里有没有那个数字,如果既有a[i+1]-1,又有a[i+1]+1,那么就相当于a[i+1]把这两段给连了起来,总的值减一,其他的也可以这么推出来。
AC代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define maxn 100010
using namespace std;
struct que{
    int l;
    int r;
    int pos;
}qu[maxn];
int a[maxn],pos[maxn],ans,n,m,cnt[maxn];    //a记录原始数据,pos记录每个数字出现的位置,以便于分块,cnt用来记录最后的结果
bool vis[maxn];
int cmp(que a,que b)
{
    if(pos[a.l] == pos[b.l])    //如果左端点在同一个区块里面,按右端点从小到大排列
        return a.r<b.r;
    return pos[a.l] < pos[b.l];   //否则按左端点区块递增排列
}
void update(int x,int k)    //往询问的区间里添加或者删减元素,k==1是加,k==0是减
{
    vis[x] = k;
    if(k)
        ans += 1-vis[x-1] - vis[x+1];  //如果x前面的和后面的数字都已经在区间里面,那么x把两个区间合成了一个,总体减一
    else
        ans += vis[x-1] + vis[x+1] - 1;//如果x-1与x+1在区间里面,x就把整个区间分隔开了,整体加一
}
int main(){
    int t;
    scanf("%d",&t);
    while(t--)
    {
        memset(vis,0,sizeof(vis));
        scanf("%d%d",&n,&m);
        int block = ceil(sqrt(n));
        for(int i = 1; i <= n; i++)
        {
            scanf("%d",&a[i]);
            pos[i] = (i-1)/block;     //pos记录i这个位置在哪个区块
        }
        for(int i = 0; i < m; i++)
        {
            scanf("%d%d",&qu[i].l,&qu[i].r);
            qu[i].pos = i;
        }
        sort(qu,qu+m,cmp);
        ans = 0;
        int ll,rr;
        rr = 0;
        ll = 1;
        for(int i = 0; i < m; i++)
        {
            int tem = qu[i].pos;
            if(rr<qu[i].r)
                for(int j = rr+1; j <= qu[i].r; j++)
                update(a[j],1);
            if(rr > qu[i].r)
                for(int j = rr; j > qu[i].r; j--)
                update(a[j],0);
            if(ll < qu[i].l)
                for(int j = ll; j < qu[i].l; j++)
                update(a[j],0);
            if(ll > qu[i].l)
                for(int j = ll-1; j >= qu[i].l; j--)
                update(a[j],1);
            ll = qu[i].l;
            rr = qu[i].r;
            cnt[tem] = ans;
        }
        for(int i = 0; i < m; i++)
            printf("%d\n",cnt[i]);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值