HDU - 5517(预处理+树状数组)Triple

Triple 2015ACM/ICPC亚洲区沈阳站

题目链接:hdu - 5517

题意

         \;\;\;\; 一个多集(multi-set) A A A 中含有 n n n 个二维元素 ( a , b ) (a,b) (a,b), 另一个多集 B B B 中含有 m m m 个三维元素 ( c , d , e ) (c,d,e) (c,d,e). 现定义多集 C = A ∗ B = { ( a , c , d ) ∣ ( a , b ) ∈ A , ( c , d , e ) ∈ B , b = e } C = A * B = \left \{ (a,c,d) | (a,b) \in A, (c,d,e) \in B, b = e\right \} C=AB={(a,c,d)(a,b)A,(c,d,e)B,b=e}.
         \;\;\;\; 问:在集合C中,有多少个元素(a,c,d),满足:在多集 C C C 中,不存在另一个元素 ( u , v , w ) ≠ ( a , c , d ) , u ⩾ a , v ⩾ c , w ⩾ d (u,v,w) \neq (a,c,d),u \geqslant a, v \geqslant c,w \geqslant d (u,v,w)̸=(a,c,d),ua,vc,wd.
1 ≤ n ≤ 1 0 5 , 1 ≤ m ≤ 1 0 5 , 1 ≤ a i , b i , e i ≤ 1 0 5 , 1 ≤ c i , d i ≤ 1 0 3 1 \leq n \leq 10^{5}, 1 \leq m \leq 10^{5}, 1 \leq a_{i}, b_{i}, e_{i} \leq 10^{5}, 1 \leq c_{i}, d_{i} \leq 10^{3} 1n105,1m105,1ai,bi,ei105,1ci,di103.

思路

1.求集合 C C C 中元素
         \;\;\;\; 集合 A A A 中元素与集合 B B B 中元素结合的条件, b = e b=e b=e,而对于集合 A A A 中有着相同 b b b 的元素,对答案有贡献的只有 a a a 最大的那个元素;在输入集合 A A A 的过程中,记录每个 b b b 对应的最大的 a a a.
         \;\;\;\; 在输入集合 B B B 的过程中,判断对于当前元素 ( c , d , e ) (c,d,e) (c,d,e) e e e, 在集合 A A A 中是否存在一样的 b b b, 如果存在的话,向集合 C C C 中添加一个元素。

2.求满足条件的元素
         \;\;\;\; 观察数据范围,发现集合 C C C 中, a a a 的范围较大, c , d c,d c,d 范围较小(这一定是在暗示什么)。
         \;\;\;\; 可将集合 C C C 中的元素,按照 a a a 由大到小排序,维护一个 c , d c,d c,d 的二维树状数组, 每次查询 ( c , d ) → ( 1 0 5 , 1 0 5 ) (c,d) \rightarrow (10^{5},10^{5}) (c,d)(105,105) 点的数量,如果为 0 0 0 说明当前元素满足条件。
         \;\;\;\; 按照 x x x 从小到大排序,维护一个关于坐标 y y y 的树状数组。遇见一个填充点操作 ( x , y ) (x,y) (x,y),就更新树状数组 u p d a t e ( y , z ) update(y,z) update(y,z),遇到一个查询操作,就加上或者减去前缀和 Q u e r y ( y − 1 ) Query(y-1) Query(y1).
         \;\;\;\; 有个需要注意的点就是,集合 A , B , C A,B,C A,B,C 都是 multi-set,可以有重复元素,但是重复元素在最后的操作中又会产生麻烦的计算,所以,可以将相同的元素记录个数,留一个参与计算。

代码

/*
nero
2019-8-27 11:57:03
http://acm.hdu.edu.cn/showproblem.php?pid=5517

写代码之前看了别人的题解,感觉学到了好多,处理方法比我想的都好太多了,
学到了学到了 Orz
*/
#include <map>
#include <time.h>
#include <cmath>
#include <stack>
#include <queue>
#include <string>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <bits/stdc++.h>

using namespace std;

#define lson(u) (u<<1)
#define rson(u) (u<<1|1)
#define Pi acos(-1)
#define iINF 0x3f3f3f3f
#define lINF 0x3f3f3f3f3f3f3f
#define EPS 0.000000001
#define pii pair<int, int>
typedef long long ll;
typedef unsigned long long ull;

const int MAXN = 1005;

int cc[MAXN][MAXN], Max[100005], Max_cnt[100005];

struct node {
    int a, b, c;
    ll num;
    bool operator < (const node &C2) const {
        if(a != C2.a) return a > C2.a;
        if(b != C2.b) return b > C2.b;
        return c > C2.c;
    }
    bool operator == (const node &C2) const {
        if(a == C2.a && b == C2.b && c == C2.c) return 1;
        else return 0;
    }
}C[100005];

int lowbit(int x) {
    return x & (-x);
}
void update(int x, int y) {
    int i = x;
    while(i < MAXN) {
        int j = y;
        while(j < MAXN) {
            cc[i][j]++;
            j += lowbit(j);
        }
        i += lowbit(i);
    }
    return;
}
int Query(int x, int y) {
    int i = x;
    int sum = 0;
    while(i > 0) {
        int j = y;
        while(j > 0) {
            sum += cc[i][j];
            j -= lowbit(j);
        }
        i -= lowbit(i);
    }
    return sum;
}
int main()
{
    int T;
    scanf("%d", &T);
    for(int tt = 1; tt <= T; tt++) {
        int n, m;
        int a, b, c, d, e;
        memset(Max, 0, sizeof(Max));
        int cnt = 0;
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i++) {
            scanf("%d%d", &a, &b);
            if(a > Max[b]) Max[b] = a, Max_cnt[b] = 1; // 去重 + 计数 *QQ*
            else if(a == Max[b]) Max_cnt[b]++;
        }
        for(int i = 1; i <= m; i++) {
            scanf("%d%d%d", &c, &d, &e);
            if(Max[e]) {
                C[++cnt] = node{Max[e], c, d, (ll)Max_cnt[e]}; // 匹配 *QQ*
            }
        }
        sort(C + 1, C + cnt + 1);
        int N = 1;
        for(int i = 2; i <= cnt; i++) {         // 去重 + 计数  *QQ*
            if(C[i] == C[N]) C[N].num += C[i].num;
            else C[++N] = C[i];
        }
        ll ans = 0;
        memset(cc, 0, sizeof(cc));
        for(int i = 1; i <= N; i++) {
            int sum = Query(1001 - C[i].b, 1001 - C[i].c); // 求大  *QQ*
            if(sum == 0) ans += C[i].num;
            update(1001 - C[i]. b, 1001 - C[i].c); // 更新  *QQ*
        }
        printf("Case #%d: %lld\n", tt, ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值