【JZOJ 3854】分组

Description

Bsny所在的精灵社区有n个居民,每个居民有一定的地位和年龄,ri表示第i个人的地位,ai表示第i个人的年龄。
最近社区里要举行活动,要求几个人分成一个小组,小组中必须要有一个队长,要成为队长有这样的条件:
1、队长在小组中的地位应该是最高的(可以并列第一);
2、小组中其他成员的年龄和队长的年龄差距不能超过K。
有些人想和自己亲密的人组在同一个小组,同时希望所在的小组人越多越好。比如x和y想在同一个小组,同时希望它们所在的小组人越多越好,当然,它们也必须选一个符合上述要求的队长,那么问你,要同时包含x和y的小组,最多可以组多少人?

Solution

先把每个人按地位排序,这样就可以预处理出每个人做组长可以领导多少人,(这里我用了树状数组)
再把输入的问题也按两个人中地位最大的排序,离线处理,
对于每个人可以确认一个年龄区间,要保证组长一定在这个年龄范围内,
用一个指针指向每一组人,按全部人的地位从大到小循环,看一下指向的组两人的最大地位是否大于当前的人,是则结算当前的人答案,指针后移,最后加上当前人的影响,(鄙人的语文辣鸡,大家可以看看代码理解一下)
这个可以用线段树,

复杂度: O(nlog(n))

Code

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define NX(x) ((x)&(-(x)))
using namespace std;
const int N = 1e5+500;
int read(int &n)
{
    char ch=' ';int q=0,w=1;
    for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
    if(ch=='-')w=-1,ch=getchar();
    for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int n,m,m1;
int zx[N],ans[N];
struct qqww
{
    int rk,a,i,f,zx;
}a[N];
int A[N];
int F[N];
struct qwqw
{
    int x,y,i;
}sc[N];
int b[4*N];
bool PX(qqww q,qqww w){return q.rk<w.rk || q.rk==w.rk&&q.a<w.a;}
bool PPX(qwqw q,qwqw w){return a[q.x].rk<a[w.x].rk;}
int SUM(int q)
{
    int ans=0;
    for(;q>0;q-=NX(q))ans+=F[q];
    return ans;
}
void ADD(int q){for(;q<=m1;q+=NX(q))F[q]++;}
int find(int l,int r,int e,int l1,int r1)
{
    if(l==l1&&r==r1)return b[e];
    int t=(l+r)>>1;
    if(r1<=t)return find(l,t,e*2,l1,r1);
        else if(t<l1)return find(t+1,r,e*2+1,l1,r1);
            else return max(find(l,t,e*2,l1,t),find(t+1,r,e*2+1,t+1,r1));
}
void change(int l,int r,int e,int l1,int l2)
{
    if(l==r){b[e]=max(b[e],l2);return;}
    int t=(l+r)>>1;
    if(l1<=t)change(l,t,e*2,l1,l2);
        else change(t+1,r,e*2+1,l1,l2);
    b[e]=max(b[e*2],b[e*2+1]);
}
int main()
{
    int q,w,_;
    read(n),read(m);
    fo(i,1,n)read(a[i].rk),a[i].i=i;
    fo(i,1,n)A[i]=read(a[i].a);
    sort(a+1,a+1+n,PX);
    fo(i,1,n)zx[a[i].i]=i;
    sort(A+1,A+1+n);
    m1=1;
    fo(i,2,n)if(A[i]!=A[i-1])A[++m1]=A[i];
    int t=1;
    fo(i,1,n)
    {

        a[i].zx=lower_bound(A+1,A+1+m1,a[i].a)-A;
        ADD(a[i].zx);
        if(a[i].rk!=a[i+1].rk)
        while(t<=i)
        {
            q=lower_bound(A+1,A+1+m1,a[t].a-m)-A;
            w=upper_bound(A+1,A+1+m1,a[t].a+m)-A-1;
            a[t].f=SUM(w)-SUM(q-1),t++;
        }
    }
    // fo(i,1,n)printf("%d ",a[i].f);printf("\n");
    read(_);
    int __=_;
    fo(i,1,_)
    {
        read(q),read(w);q=zx[q],w=zx[w];
        if(a[q].rk<a[w].rk)swap(q,w);
        sc[i].x=q,sc[i].y=w;sc[i].i=i;
    }
    sort(sc+1,sc+1+_,PPX);
    fod(i,n,0)
    {
        for(;a[i].rk<a[sc[_].x].rk;_--)
        {
            if(a[sc[_].x].a<a[sc[_].y].a)swap(sc[_].x,sc[_].y);
            q=lower_bound(A+1,A+1+m1,a[sc[_].x].a-m)-A;q=max(1,q);
            w=upper_bound(A+1,A+1+m1,a[sc[_].y].a+m)-A-1;
            if(q<=w)ans[sc[_].i]=find(1,n,1,q,w);
        }
        change(1,n,1,a[i].zx,a[i].f);
    }
    fo(i,1,__)if(ans[i])printf("%d\n",ans[i]);else printf("-1\n");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值