【CF815D】Karen and Cards
题意:一张卡片有三个属性a,b,c,其上限分别为A,B,C,现在有n张卡片,定义一张卡片能打败另一张卡片当且仅当它的至少两项属性要严格大于另一张的对应属性。问在所有可能的卡片中,有多少种能打败这全部n张卡。
n,A,B,C<=500000
题解:我们反过来,统计哪些卡片不能打败全部的卡。
我们先确定一个属性c,那么对于某张卡片(ai,bi,ci),如果c<=ci,则要求!(a>ai&&b>bi);如果c>ci,则要求a<=ai&&b<=bi。这两种情况都可以用平面上的两个矩形来表示。
那么我们要做的就是动态维护这些矩形的并,发现矩形的并一定越来越小,反过来就是越来越大,我们用单调栈预处理一下,然后用扫描线统计即可。
#include <cstring>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=500010;
typedef long long ll;
ll ans,sum;
int n,A,B,C,top,tx,ty;
struct node
{
int a,b,c;
}p[maxn];
int st[maxn],x[maxn],y[maxn];
inline int rd()
{
int ret=0,f=1; char gc=getchar();
while(gc<'0'||gc>'9') {if(gc=='-') f=-f; gc=getchar();}
while(gc>='0'&&gc<='9') ret=ret*10+(gc^'0'),gc=getchar();
return ret*f;
}
bool cmpa(const node &a,const node &b)
{
return a.a<b.a;
}
bool cmpc(const node &a,const node &b)
{
return a.c>b.c;
}
int main()
{
n=rd(),A=rd(),B=rd(),C=rd();
int i,j;
for(i=1;i<=n;i++) p[i].a=rd(),p[i].b=rd(),p[i].c=rd();
sort(p+1,p+n+1,cmpa);
for(i=1;i<=n;i++)
{
while(top&&p[i].b>p[st[top]].b) top--;
st[++top]=i;
}
sum=1ll*A*B,st[top+1]=0;
for(i=1;i<=top;i++)
{
sum-=1ll*(p[st[i]].a-p[st[i-1]].a)*p[st[i]].b;
for(j=p[st[i-1]].a+1;j<=p[st[i]].a;j++) y[j]=p[st[i]].b;
for(j=p[st[i]].b;j>p[st[i+1]].b;j--) x[j]=p[st[i]].a;
}
sort(p+1,p+n+1,cmpc);
for(tx=ty=j=1,i=C;i;i--)
{
for(;j<=n&&p[j].c>=i;j++)
{
for(;tx<=p[j].a;tx++) sum-=B-max(y[tx],ty-1);
for(;ty<=p[j].b;ty++) sum-=A-max(x[ty],tx-1);
}
ans+=sum;
}
printf("%lld",ans);
return 0;
}