题目大意
给定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;
}