HDU 4638 Group (莫队算法||线段树离散查询)

题目地址:HDU 4638
先写了一发莫队,莫队可以水过。很简单的莫队,不多说。
代码如下:

#include <iostream>
#include <string.h>
#include <math.h>
#include <queue>
#include <algorithm>
#include <stdlib.h>
#include <map>
#include <set>
#include <stdio.h>
#include <time.h>
using namespace std;
#define LL long long
#define pi acos(-1.0)
#pragma comment(linker, "/STACK:1024000000")
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const double eqs=1e-9;
const int MAXN=100000+10;
int a[MAXN];
LL ha[MAXN], ans[MAXN], res;
struct node {
        int l, r, id, pos;
} fei[MAXN];
bool cmp(node x, node y)
{
        return x.pos<y.pos||x.pos==y.pos&&x.r<y.r;
}
void reduce(int tmp)
{
        if(ha[tmp-1]&&ha[tmp+1]) res++;
        else if(!ha[tmp-1]&&!ha[tmp+1]) res--;
        ha[tmp]=0;
}
void add(int tmp)
{
        if(ha[tmp-1]&&ha[tmp+1]) res--;
        else if(!ha[tmp-1]&&!ha[tmp+1]) res++;
        ha[tmp]=1;
}
int main()
{
        int t, n, m, i, j, k, tmp, l, r;
        scanf("%d",&t);
        while(t--) {
                scanf("%d%d",&n,&m);
                for(i=1; i<=n; i++) {
                        scanf("%d",&a[i]);
                }
                k=sqrt(n*1.0);
                for(i=0; i<m; i++) {
                        scanf("%d%d",&fei[i].l,&fei[i].r);
                        fei[i].id=i;
                        fei[i].pos=fei[i].l/k;
                }
                sort(fei,fei+m,cmp);
                memset(ha,0,sizeof(ha));
                l=1;
                r=0;
                res=0;
                for(i=0; i<m; i++) {
                        while(r>fei[i].r) {
                                tmp=a[r];
                                reduce(tmp);
                                r--;
                        }
                        while(r<fei[i].r) {
                                r++;
                                tmp=a[r];
                                add(tmp);
                        }
                        while(l<fei[i].l) {
                                tmp=a[l];
                                reduce(tmp);
                                l++;
                        }
                        while(l>fei[i].l) {
                                l--;
                                tmp=a[l];
                                add(tmp);
                        }
                        ans[fei[i].id]=res;
                }
                for(i=0; i<m; i++) {
                        printf("%I64d\n",ans[i]);
                }
        }
        return 0;
}

然后又看了解题报告,发现线段树+离线查询也可以过。而且感觉很神奇的做法。。
首先先离线保存下来。然后从左向右维护,维护的是前面的值对于当前枚举值的相对个数。对于当前第i个数来说,先将这个数的值更新为1,代表一个独立的串,然后找a[i]-1和a[i]+1前面存在不存在,如果存在的话,则说明前面的这两个数已经不能作为独立的串了,相对于a[i]来说,可以共存在a[i]的串中,所以就要将a[i]-1和a[i]+1分别-1.然后再在r值为i的询问中,直接线段树求和就可以了。代码如下:

#include <iostream>
#include <string.h>
#include <math.h>
#include <queue>
#include <algorithm>
#include <stdlib.h>
#include <map>
#include <set>
#include <stdio.h>
#include <time.h>
using namespace std;
#define LL long long
#define pi acos(-1.0)
#pragma comment(linker, "/STACK:1024000000")
#define root 1, n, 1
#define lson l, mid, rt<<1
#define rson mid+1, r, rt<<1|1
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const double eqs=1e-9;
const int MAXN=100000+10;
int a[MAXN], pos[MAXN], sum[MAXN<<2], ans[MAXN];
struct node
{
        int l, r, id;
}fei[MAXN];
bool cmp(node x, node y)
{
        return x.r<y.r;
}
void PushUp(int rt)
{
        sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void Update(int p, int x, int l, int r, int rt)
{
        if(l==r){
                sum[rt]+=x;
                return ;
        }
        int mid=l+r>>1;
        if(p<=mid) Update(p,x,lson);
        else Update(p,x,rson);
        PushUp(rt);
}
int Query(int ll, int rr, int l, int r, int rt)
{
        if(ll<=l&&rr>=r){
                return sum[rt];
        }
        int mid=l+r>>1, ans=0;
        if(ll<=mid) ans+=Query(ll,rr,lson);
        if(rr>mid) ans+=Query(ll,rr,rson);
        return ans;
}
int main()
{
        int t, n, m, i, j;
        scanf("%d",&t);
        while(t--){
                scanf("%d%d",&n,&m);
                for(i=1;i<=n;i++){
                        scanf("%d",&a[i]);
                        pos[a[i]]=i;
                }
                for(i=0;i<m;i++){
                        scanf("%d%d",&fei[i].l,&fei[i].r);
                        fei[i].id=i;
                }
                memset(sum,0,sizeof(sum));
                sort(fei,fei+m,cmp);
                j=0;
                for(i=1;i<=n;i++){
                        Update(i,1,root);
                        if(a[i]>1&&pos[a[i]-1]<i) Update(pos[a[i]-1],-1,root);
                        if(a[i]<n&&pos[a[i]+1]<i) Update(pos[a[i]+1],-1,root);
                        while(j<m&&fei[j].r<=i){
                                ans[fei[j].id]=Query(fei[j].l,i,root);
                                j++;
                        }
                }
                for(i=0;i<m;i++){
                        printf("%d\n",ans[i]);
                }
        }
        return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值