[codeforces815D] Karen and Cards

本文介绍了如何解决一个关于卡片属性压制的问题。给定N张卡片,每个卡片有三个属性,需要找出多少张卡片能压制其他卡片。通过枚举属性值并使用单调栈维护有效区域,结合线段树进行区间查询和更新,实现O(nlogn)的时间复杂度解决方案。
摘要由CSDN通过智能技术生成

题目大意

给定N张卡牌,每张卡牌有三个属性ai,bi,ci。
现在给出三个数p,q,r,分别表示三个属性的上限。问有多少种不同的卡牌,能压制给定的N张卡牌(只要三个属性有两个的值严格大于另一卡牌即可)。其中属性值一定是正整数。

1≤N,p,q,r≤500000

分析

可以枚举其中一个属性的值,假设枚举的是c。
那么对于ci≥c的卡牌,就一定是属性a,b去压制它。那么所有ci≤c的卡牌中,a,b各取最大值就是下限了。
然而对于ci < c的卡牌,只需满足a,b中一个属性严格大于即可。
现在可以把a,b属性看成点(a,b),那么对于ci < c,对答案有贡献的卡牌不能同时满足a≤ai,b≤bi。对于ci≥c,对答案有贡献的卡牌只出现在a>ai,b>bi的区域中。
首先考虑维护ci < c的信息。可以发现,如果出现(ai,bi),(aj,bj)满足ai≥aj,bi≥bj,那么j是没用的。有用的点按a排序后b是单调递减的。
维护一个数组g,g[i]表示a取了i后,如果b≤g[i],那么这个点对答案无贡献。容易发现插入一个点(ai,bi)就是前ai个数取最大值。
询问时,拿ci≥c的卡牌中a,b各取最大值得到的点的右上角区域,与当前的有效区域求个交集大小。这只需对b数组开线段树,查询最后一个大于某值的位置、区间和即可。
然而如果插入一个点是区间取max,维护区间和会很麻烦。然而取max的区间永远是[1,a]形式,可以分成查询最后一个大于某值的位置、区间赋值两个操作。
时间复杂度 O(nlogn)

#include <cstdio>
#include <cstring>
#include <algorithm>

#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))

using namespace std;

const int N=500005,M=1311000;

typedef long long LL;

int n,p,q,r,Ma[N],Mb[N],Tag[M],Mx[M],tmp;

LL ans,sum[M],s;

struct Data
{
    int a,b,c;
}A[N];

char c;

int read()
{
    int x=0,sig=1;
    for (c=getchar();c<'0' || c>'9';c=getchar()) if (c=='-') sig=-1;
    for (;c>='0' && c<='9';c=getchar()) x=x*10+c-48;
    return x*sig;
}

bool operator < (const Data &a,const Data &b)
{
    return a.c<b.c;
}

void Ins(int l,int r,int a,int b,int v,int x)
{
    if (l==a && r==b)
    {
        Tag[x]=Mx[x]=v;
        sum[x]=(LL)(r-l+1)*v;
        return;
    }
    int mid=l+r>>1;
    if (Tag[x]>0)
    {
        Tag[x<<1]=Tag[x<<1|1]=Mx[x<<1]=Mx[x<<1|1]=Tag[x];
        sum[x<<1]=(LL)(mid-l+1)*Tag[x]; sum[x<<1|1]=(LL)(r-mid)*Tag[x];
        Tag[x]=0;
    }
    if (b<=mid) Ins(l,mid,a,b,v,x<<1);
    else if (a>mid) Ins(mid+1,r,a,b,v,x<<1|1);
    else
    {
        Ins(l,mid,a,mid,v,x<<1); Ins(mid+1,r,mid+1,b,v,x<<1|1);
    }
    sum[x]=sum[x<<1]+sum[x<<1|1];
    Mx[x]=max(Mx[x<<1],Mx[x<<1|1]);
}

void Get(int l,int r,int g,int x)
{
    if (l==r)
    {
        tmp=l; return;
    }
    int mid=l+r>>1;
    if (Tag[x]>0)
    {
        Tag[x<<1]=Tag[x<<1|1]=Mx[x<<1]=Mx[x<<1|1]=Tag[x];
        sum[x<<1]=(LL)(mid-l+1)*Tag[x]; sum[x<<1|1]=(LL)(r-mid)*Tag[x];
        Tag[x]=0;
    }
    if (Mx[x<<1|1]<g) Get(l,mid,g,x<<1);else Get(mid+1,r,g,x<<1|1);
}

void getsum(int l,int r,int a,int b,int x)
{
    if (l==a && r==b)
    {
        s+=sum[x]; return;
    }
    int mid=l+r>>1;
    if (Tag[x]>0)
    {
        Tag[x<<1]=Tag[x<<1|1]=Mx[x<<1]=Mx[x<<1|1]=Tag[x];
        sum[x<<1]=(LL)(mid-l+1)*Tag[x]; sum[x<<1|1]=(LL)(r-mid)*Tag[x];
        Tag[x]=0;
    }
    if (b<=mid) getsum(l,mid,a,b,x<<1);else if (a>mid) getsum(mid+1,r,a,b,x<<1|1);else
    {
        getsum(l,mid,a,mid,x<<1); getsum(mid+1,r,mid+1,b,x<<1|1);
    }
}

int main()
{
    n=read(); p=read(); q=read(); r=read();
    for (int i=1;i<=n;i++)
    {
        A[i].a=read(); A[i].b=read(); A[i].c=read();
    }
    sort(A+1,A+n+1);
    for (int i=n;i;i--)
    {
        Ma[i]=max(Ma[i+1],A[i].a); Mb[i]=max(Mb[i+1],A[i].b);
    }
    Ins(0,p,0,0,q+1,1);
    for (int i=1,j=1,k;i<=r;i++)
    {
        for (;j<=n && A[j].c<i;j++)
        {
            Get(0,p,A[j].b,1);
            if (tmp>=A[j].a) continue;
            Ins(0,p,tmp+1,A[j].a,A[j].b,1);
        }
        Get(0,p,Mb[j]+1,1);
        tmp=max(tmp,Ma[j]);
        s=0;
        if (tmp>Ma[j]) getsum(0,p,Ma[j]+1,tmp,1);
        ans+=(LL)(tmp-Ma[j])*q-s+(LL)(p-tmp)*(q-Mb[j]);
    }
    printf("%I64d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值