题意:给你n(n<=1e5)个正整数二元组<a,b> (1<=a,b<=1e5)
给你m(m<=1e5)个正整数三元组<c,d,e>(1<=c,d<=1e3 1<=e<=1e5)
定义新的三元组等于上述两个二元组的“积”,运算规则如下:
<a,b> * <c,d,e> =<a,c,d> if b==e
求新的所有三元组中,有多少个三元组在凸点.(没有其它三元组比它大)
思路:本题的关键是数据范围,注意我红色标记的部分。
注意到以后,求凸点就比较容易想到二维树状数组了。但是直接暴力相乘两个二元组的话会爆炸。
注意到新的三元组的定义。也就是说二元组中b相同的,只有最大的a才会对答案有贡献。(想想是不是)
因此对于每个b,只保留最大的a。相同的累加数量。
这样和三元组做“积”之后,新的三元组的数量不会超过1e5。然后合并重复的三元组。
然后就是二维树状数组求凸点的操作了。
按a从小到大排序,相同b小的排前面,再相同c小的排前面。
然后倒着往二维树状数组里加点(b,c),如果它右上角有比他大的点,则这个新三元组对答案没贡献。
注意long long。
代码:
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f3f3f3f3fLL
using namespace std;
const int maxn=1010;
const int maxm=100010;
ll C[maxn][maxn];
int n,m;
int mp[maxm];
ll num[maxm];
struct node
{
int a,b,c;
ll sum;
node(){}
node(int aa,int bb,int cc,ll dd)
{
a=aa;b=bb;c=cc;sum=dd;
}
bool operator<(node aa)const
{
if(aa.a!=a) return a<aa.a;
else if(aa.b!=b) return b<aa.b;
else return c<aa.c;
}
node operator +(const node &aa)const
{
return node(a,b,c,sum+aa.sum);
}
bool operator ==(const node &aa)const
{
return (!(*this<aa||aa<*this));
}
}st[maxm];
int lb(int x){return x&(-x);}
void add(int x,int y,ll v)
{
for(int i=x;i<maxn;i+=lb(i))
for(int j=y;j<maxn;j+=lb(j))
C[i][j]+=v;
}
ll query(int x,int y)
{
ll ans=0;
for(int i=x;i>0;i-=lb(i))
for(int j=y;j>0;j-=lb(j))
{
ans+=C[i][j];
}
return ans;
}
int main()
{
int T,cas=1;
scanf("%d",&T);
int x,y;
while(T--)
{
scanf("%d%d",&n,&m);
memset(C,0,sizeof(C));
memset(mp,0,sizeof(mp));
memset(num,0,sizeof(num));
for(int i=0;i<n;i++)
{
scanf("%d%d",&x,&y);
if(mp[y]<x) {mp[y]=x;num[y]=1;}
else if(mp[y]==x){num[y]++;}
}
int cnt=0;
for(int i=0;i<m;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
if(mp[z]) st[cnt++]=node(mp[z],x,y,num[z]);
}
sort(st,st+cnt);
int tot=0;
for(int i=1;i<cnt;i++)
{
if(st[tot]==st[i]) st[tot]=st[tot]+st[i];
else st[++tot]=st[i];
}
ll ans=0;
for(int i=tot;i>=0;i--)
{
int x=st[i].b,y=st[i].c;
ll f=query(1000,1000)-query(x-1,1000)-query(1000,y-1)+query(x-1,y-1);
if(!f) ans+=st[i].sum;
add(x,y,1);
}
printf("Case #%d: %lld\n",cas++,ans);
}
return 0;
}