hdu 4638 Group (莫队算法 || 离线线段树)

题目大意:

n个人的编号是从1 - n  ,现在他们无序的站成一排。

第 id 号人和 id-1  id +1 号人是朋友,

朋友之间可以组成group。

一个group的值等于他们人数的平方。

然后有m次询问,问给出的l r 之间能构成group值的和的最大值的group数。


思路分析 (莫队算法):

首先,我们面临着假设你知道这个区间有多少个group 你能知道得到最大值的时候group是怎么分配的么。

令 x = a + b

x^2 = (a+b)^2 = a^2 + 2ab + b^2

可以得到如果得到了group 那么就不要分裂,因为group在一起的才能组成最大值。


那么现在解决询问的问题。

我们用分块莫队算法直接跑。

用ex 记录哪个id的人存在了。

加入的时候通过判断左右两人是否存在。

然后仔细推一下就知道加入与删除的关系了。


(离线线段树)

我们假设所有新加入到树中的数就是孤立的。然后我们再去判断他左边右边是否在树中,如果在树中那么就取消他们所在区间的标记。

借着

5 1

2 1 4 3 5

1 5

更好理解这份代码。



#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>

#define maxn 100005

using namespace std;

int bel[maxn];
int block;

inline void scanf_(int &num){
    char in;
    bool neg=false;
    while(((in=getchar()) > '9' || in<'0') && in!='-') ;
    if(in=='-'){
        neg=true;
        while((in=getchar()) >'9' || in<'0');
    }
    num=in-'0';
    while(in=getchar(),in>='0'&&in<='9')
        num*=10,num+=in-'0';
    if(neg)
        num=0-num;
}

inline void printf_(int num){
    bool flag=false;
    if(num<0){
        putchar('-');
        num=-num;
    }
    int ans[10],top=0;
    while(num!=0){
        ans[top++]=num%10;
        num/=10;
    }
    if(top==0)
        putchar('0');
    for(int i=top-1;i>=0;i--){
        char ch=ans[i]+'0';
        putchar(ch);
    }
}
struct node
{
    int st,ed,ans,id;
    bool operator < (const node &cmp)const
    {
        return bel[st]<bel[cmp.st] || (bel[st]==bel[cmp.st] && ed<cmp.ed);
    }
}Q[maxn];
bool cmp_id(node a,node b)
{
    return a.id<b.id;
}
int a[maxn];
int ex[maxn];

void modify(int num,int &tans,int add)
{
    if(add==1)
    {
        if(ex[num-1] && ex[num+1])tans--;
        else if(!ex[num-1] && !ex[num+1])tans++;
    }
    else
    {
        if(ex[num-1] && ex[num+1])tans++;
        else if(!ex[num-1] && !ex[num+1])tans--;
    }
    ex[num]+=add;
}
int main()
{
    int T;
    scanf_(T);
    while(T--)
    {
        memset(ex,0,sizeof ex);
        
        int n,m;
        scanf_(n),scanf_(m);
        block=(int)sqrt(1.0*n);

        for(int i=1;i<=n;i++)
        {
            scanf_(a[i]);
            bel[i]=(i-1)/block+1;
        }
        for(int i=1;i<=m;i++)
        {
            scanf_(Q[i].st);
            scanf_(Q[i].ed);
            Q[i].id=i;
        }
        sort(Q+1,Q+1+m);
        int tans=0;
        for(int l=1,r=0,i=1;i<=m;i++)
        {
            if(r<Q[i].ed)
            {
                for(r=r+1;r<=Q[i].ed;r++)
                    modify(a[r],tans,1);
                r--;
            }
            if(r>Q[i].ed)
            {
                for(;r>Q[i].ed;r--)
                    modify(a[r],tans,-1);
            }
            if(l<Q[i].st)
            {
                for(;l<Q[i].st;l++)
                    modify(a[l],tans,-1);
            }
            if(l>Q[i].st)
            {
                for(l=l-1;l>=Q[i].st;l--)
                    modify(a[l],tans,1);
                l++;
            }
            Q[i].ans=tans;
        }
        sort(Q+1,Q+1+m,cmp_id);
        for(int i=1;i<=m;i++)
        {
            printf_(Q[i].ans);
            puts("");
        }
    }
    return 0;
}

离线线段树。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define lson num<<1,s,mid
#define rson num<<1|1,mid+1,e
#define mid ((s+e)>>1)
#define maxn 100005

using namespace std;

int cnt[maxn<<2];

void update(int num,int s,int e,int pos,int val)
{
    if(s==e){
        cnt[num]+=val;
        return;
    }
    if(pos<=mid)update(lson,pos,val);
    else update(rson,pos,val);
    cnt[num]=cnt[num<<1]+cnt[num<<1|1];
}
int query(int num,int s,int e,int l,int r)
{
    if(l<=s && r>=e)return cnt[num];
    if(r<=mid)return query(lson,l,r);
    else if(l>mid)return query(rson,l,r);
    else return query(lson,l,mid)+query(rson,mid+1,r);
}
struct node
{
    int st,ed,id,ans;
    bool operator < (const node &cmp)const
    {
        return ed<cmp.ed;
    }
}Q[maxn];
bool cmp_id(node a,node b)
{
    return a.id<b.id;
}
int pos[maxn];
int a[maxn];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            pos[a[i]]=i;
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&Q[i].st,&Q[i].ed);
            Q[i].id=i;
        }
        sort(Q+1,Q+1+m);
        int index=1;
        memset(cnt,0,sizeof cnt);
        for(int i=1;i<=n;i++)
        {
            update(1,1,n,pos[a[i]],1);
            if(pos[a[i]+1]<=i && a[i]<n)update(1,1,n,pos[a[i]+1],-1);
            if(pos[a[i]-1]<=i && a[i]>1)update(1,1,n,pos[a[i]-1],-1);
            while(index<=m && Q[index].ed==i)
            {
                Q[index].ans=query(1,1,n,Q[index].st,Q[index].ed);
                index++;
            }
        }
        sort(Q+1,Q+1+m,cmp_id);
        for(int i=1;i<=m;i++)
            printf("%d\n",Q[i].ans);
    }
    return 0;
}



  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值