HDU 5517 Triple (线段树)

题意:

        有二元组(a,b),三元组(c,d,e)。当b == e时它们能构成(a,c,d)。 
        然后,当不存在(u,v,w)!=(a,b,c)且u>=a,v>=b,w>=c时,则是一个better集合里的元素。 
        问这个better集合有几个元素

思路:

        注意到数据范围
        对于 N 个二元组(a,b), M 个三元组(c,d,e)
        N,M≤1e5
        ai,bi≤1e5
        ci,di≤1e3 ei≤1e5
        假设所有bi和ei都相等
        简单的两两组合会组合出1e5*1e5种 (a,c,d)

        但考虑到条件:
        求出所有(u,v,w)!=(a,b,c)且u>=a,v>=b,w>=c
        有对于所有的 (a,b) 若存在 bi==bj 只有ai >= aj
        的点对才有可能组成新的better (a,c,d)
        即对于一类 bx 或 ex只会存在一个有效的ax
        即对于任意一个三元组(c,d,e)只会有一个ax与之组合为新的(a,c,d)
        故至多有1e5个可能的better(a,c,d)
        那么问题就变成了在1e5(a,c,d)中找better

        对于1e5个可能的better(a,c,d)
        考虑到条件:
        求出所有(u,v,w)!=(a,c,d)且u>=a,v>=c,w>=d
        我们可将其按a大在前的顺序排序
        对于之后的每个(a,c,d)都可以保证u>=a
        那么问题就变成了在1e5(c,d)中找better
        考虑到ci,di≤1e3 
        我们可以利用线段树维护对于每一种ci对应的最大值di
        做1e5次区间查询和单点更新
        对于每种(ci,di)先查询 [c,max(ci)]是否存在 dj>=di
        若存在
        则说明所有的(ci,di)(一种(ci,di)可能不止有一个)都是better
        查询后再更新ci对应的最大值di即可

代码:

#include <bits/stdc++.h>
using namespace std;
#define ls l,mid,rt*2
#define rs mid+1,r,rt*2+1
#define sf l,r,rt
const int MAXN=1e3+100;
const int MAXNN=1e5+5;
typedef struct Node{
    int x,y,z;
    bool operator < (const Node &a)const{
        if(x==a.x) return y==a.y?z>a.z:y>a.y;
        return x>a.x;
    }
}Node;
int tree[4*MAXN],fast[MAXN],st,en,v,ans,fans;
int t[MAXNN],cnt[MAXNN];
vector <Node> a[MAXNN];
map <Node,int> mp;
void build(int l,int r,int rt){
    tree[rt]=0;
    if(l==r){fast[l]=rt;return;}
    int mid=(l+r)/2;
    build(ls);
    build(rs);
}
void update(int rt){
    while(rt!=0&&tree[rt]<v){
        tree[rt]=v;
        rt/=2;
    }
}
void query(int l,int r,int rt){
    if(st>r||en<l||tree[rt]<=ans) return ;
    if(st<=l&&r<=en) ans=tree[rt];
    int mid=(l+r)/2;
    query(ls);
    query(rs);
}
void ini(){
    for(int i=1;i<MAXNN-1;i++)
        t[i]=cnt[i]=0,a[i].clear();
    mp.clear();
    en=-1;
    fans=0;
}
int main()
{
    int T,n,m,x,y,z;
    scanf("%d",&T);
    for(int Case=1;Case<=T;Case++){
        ini();
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%d%d",&x,&y);
            if(t[y]<x) t[y]=x,cnt[y]=1;
            else if(t[y]==x) cnt[y]++;
        }
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&x,&y,&z);
            if(t[z]!=0){
                if(!mp[{t[z],x,y}])
                    a[t[z]].push_back({t[z],x,y});
                mp[{t[z],x,y}]+=cnt[z];
                en=max(en,x);
            }
        }
        build(1,en,1);
        int len;
        for(int i=MAXNN-1;i>0;i--){
            len=a[i].size();
            if(len){
                sort(a[i].begin(),a[i].end());
                for(int j=0;j<len;j++){
                    ans=0;st=a[i][j].y;
                    query(1,en,1);
                    if(ans<a[i][j].z)
                        fans+=mp[a[i][j]];
                    v=a[i][j].z;
                    update(fast[a[i][j].y]);
                }
            }
        }
        printf("Case #%d: %d\n",Case,fans);
    }
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值