【JZOJ 5229】【GDOI2018模拟7.14】小奇的糖果

解决一个算法问题,关于在平面上寻找最优水平线段以收集尽可能多但不包含所有颜色的糖果。采用分治策略和主席树数据结构实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

有 N 个彩色糖果在平面上。小奇想在平面上取一条水平的线段,并拾起它上方或下方的所有糖果。求出最多能够拾起多少糖果,使得获得的糖果并不包含所有的颜色。

Solution1

指向【一大堆常犯的错误、提醒和公式】的第20条;

枚举每个颜色,把向上取和向下取分开讨论,
对于向上取,显然是先找最高的,把它以上的其他颜色点取完,再向这个点在坐标中位置的左边右边分治下去,

这个用主席树即可,

复杂度: O(nlog(n)) (常数很大啊)
注意优化:当发现就算把当前区间的点取完也不能使Ans更优,直接退出

Solution2

同样枚举每个颜色I,不同的是,这里像是上面分治倒着做,同样把向上取和向下取分开讨论,
对于向上取,维护扫描线,枚举颜色I,每个颜色为I的点,记录他当前左、右边的第一个点,那么它的答案就是这个区间的所有点(不包当前行),

Code

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define min(q,w) ((q)>(w)?(w):(q))
#define max(q,w) ((q)<(w)?(w):(q))
using namespace std;
const int N=100050;
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 m,n,Ans,R,TOP;
int px[N],py[N];
struct qqww
{
    int x,y,k;
}a[N];
struct qwqw
{
    int l,r,mx,mx1,mi,mi1;
}b[N*30];
int root[N],b0;
struct qwqwH
{
    int l,r,v,ti;
}bH[N*30];
int rootH[N],bH0;
int rsum[N];
bool zk[N],TI;
bool PXx(int q,int w){return a[q].x<a[w].x;}
bool PXy(int q,int w){return a[q].y<a[w].y;}
int build(int l,int r,int e,int l1,int l2)
{
    if(!e)
    {
        e=++b0;
        b[e].mx=-2e9;b[e].mi=2e9;
        b[e].mx1=b[e].mi1=b[e].l=b[e].r=0;
    }
    if(l==r)
    {
        b[e].mx=max(b[e].mx,l2);
        b[e].mi=min(b[e].mi,l2);
        b[e].mx1=b[e].mi1=l;
        return e;
    }
    int t=(l+r)>>1;
    if(l1<=t)b[e].l=build(l,t,b[e].l,l1,l2);
    else b[e].r=build(t+1,r,b[e].r,l1,l2);
    if(b[b[e].l].mx>b[b[e].r].mx)b[e].mx=b[b[e].l].mx,b[e].mx1=b[b[e].l].mx1;
        else b[e].mx=b[b[e].r].mx,b[e].mx1=b[b[e].r].mx1;
    if(b[b[e].l].mi<b[b[e].r].mi)b[e].mi=b[b[e].l].mi,b[e].mi1=b[b[e].l].mi1;
        else b[e].mi=b[b[e].r].mi,b[e].mi1=b[b[e].r].mi1;
    return e;
}
int buildH(int l,int r,int e1,int e,int I,int l1)
{
    if(!e||bH[e].ti<I)
    {
        e=++bH0;
        bH[e].l=bH[e1].l;
        bH[e].r=bH[e1].r;
        bH[e].v=bH[e1].v;
        bH[bH0].ti=I;
    }
    if(l==r){bH[e].v++;return e;}
    int t=(l+r)>>1;
    if(l1<=t)bH[e].l=buildH(l,t,bH[e1].l,bH[e].l,I,l1);
    else bH[e].r=buildH(t+1,r,bH[e1].r,bH[e].r,I,l1);
    bH[e].v=bH[bH[e].l].v+bH[bH[e].r].v;
    return e;
}
int fmx,fmx1,fmi,fmi1;
void find(int l,int r,int e,int l1,int r1)
{
    if(!e||l>r||l1>r1)
    {
        fmx=b[0].mx;fmx1=b[0].mx1;
        fmi=b[0].mi;fmi1=b[0].mi1;
        return;
    }
    if(l>=l1&&r<=r1)
    {
        fmx=b[e].mx;fmx1=b[e].mx1;
        fmi=b[e].mi;fmi1=b[e].mi1;
        return;
    }
    int t=(l+r)>>1;
    if(r1<=t)find(l,t,b[e].l,l1,r1);
    else if(t<l1)find(t+1,r,b[e].r,l1,r1);
    else
    {
        find(l,t,b[e].l,l1,t);
        int qmx=fmx,qmx1=fmx1,qmi=fmi,qmi1=fmi1;
        find(t+1,r,b[e].r,t+1,r1);
        if(fmx<qmx)fmx=qmx,fmx1=qmx1;
        if(fmi>qmi)fmi=qmi,fmi1=qmi1;
    }
}
int findH(int l,int r,int e1,int e,int l1,int r1)
{
    if(!e||l1>r1||l>r)return 0;
    if(l>=l1&&r<=r1)return bH[e].v-bH[e1].v;
    int t=(l+r)>>1;
    if(r1<=t)return findH(l,t,bH[e1].l,bH[e].l,l1,r1);
    if(t<l1)return findH(t+1,r,bH[e1].r,bH[e].r,l1,r1);
    return findH(l,t,bH[e1].l,bH[e].l,l1,t)+findH(t+1,r,bH[e1].r,bH[e].r,t+1,r1);
}
void divideU(int I,int l,int r)
{
    if(l>r)return;
    if(rsum[r]-rsum[l-1]<=Ans)return;
    find(1,R,root[I],l,r);
    int qmx1=fmx1;
    Ans=max(Ans,findH(1,TOP,rootH[l-1],rootH[r],fmx+1,TOP));
    if(qmx1)divideU(I,l,qmx1-1),divideU(I,qmx1+1,r);
}
void divideD(int I,int l,int r)
{
    if(l>r)return;
    if(rsum[r]-rsum[l-1]<=Ans)return;
    find(1,R,root[I],l,r);
    int qmi1=fmi1;
    Ans=max(Ans,findH(1,TOP,rootH[l-1],rootH[r],1,fmi-1));
    if(qmi1)divideD(I,l,qmi1-1),divideD(I,qmi1+1,r);
}
int main()
{
    int _;b[0].mx=-2e9;b[0].mi=2e9;
    read(_);
    while(_--)
    {
        read(n),read(m);
        Ans=0;b0=bH0=0;
        fo(i,1,m)root[i]=0;
        TI++;
        fo(i,1,n)read(a[i].x),read(a[i].y),zk[read(a[i].k)]=TI,px[i]=py[i]=i;
        fo(i,1,m)if(zk[i]<TI)m=0;
        if(!m){printf("%d\n",n);continue;}
        sort(px+1,px+1+n,PXx);
        sort(py+1,py+1+n,PXy);
        R=TOP=1;a[0].x=a[0].y=2333333;
        fo(i,1,n)
        {
            int w=R;
            if(a[px[i+1]].x!=a[px[i]].x&&i<n)R++;
            a[px[i]].x=w;rsum[w]++;
            w=TOP;
            if(a[py[i+1]].y!=a[py[i]].y&&i<n)TOP++;
            a[py[i]].y=w;
        }
        fo(i,1,R)rootH[i]=0,rsum[i]+=rsum[i-1];
        fo(i,1,n)
        {
            root[a[i].k]=build(1,R,root[a[i].k],a[i].x,a[i].y);
            rootH[a[px[i]].x]=buildH(1,TOP,rootH[a[px[i]].x-1],rootH[a[px[i]].x],a[px[i]].x,a[px[i]].y);
        }
        fo(i,1,m)divideU(i,1,R),divideD(i,1,R);
        printf("%d\n",Ans);
        fo(i,1,R)rsum[i]=0;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值