hdu5571Triple(二维树状数组)

题意: 有二元组集{(a,b)}和三元组集{(c,d,e)}。当 (a,b)和(c,d,e) 满足 b==e 时,(a,c,d)组成新的三元组集 C {(a,b,c)}。
计算三元组集 C 中 满足 不存在除与本身相等外满足(u>=a,v>=b,w>=c)的三元组(u,v,w) 的三元组(a,b,c)。
思路:
自己写的时候完全没有思路啊~
参考了http://async.icpc-camp.org/d/227-shenyang-2015-i
http://blog.csdn.net/beihai2013/article/details/49588259
首先对于二元组集{(a,b)},预处理出 b 对应的最大 a 值和个数。
接着与三元组集{(c,d,e)}结合组成新的三元组C {(a,b,c,t)}(其中t表示这样的三元组的个数)。
对于新三元组集C {(a,b,c)},预处理出 a 对应的极大值 (c,d)值和个数 (对 b 进行从大到小排序,相同的b只取最大 c 值并记录个数,然后根据 b 从大到小遍历一遍,之前的 c >= 当前位置的 c,那么这个三元组(a,b,c)是无效的)。现在,对于每个三元组(a,b,c),都是关于 a 的局部最大,所以对a从大到小排序,遍历,用二维树状数组处理是否存在大于等于当前(b,c)的三元组(此时不会出现(a,b,c)完全相等,所以如果出现(b,c)相等,说明之前出现的那个的 a 更大)。边算边加答案,但这样的实现有点麻烦,所以没有用这种方法。

重新考虑一下,对a从大到小排序,遍历,用二维树状数组处理是否存在大于等于当前(b,c)的三元组。如果对三元组按a,b,c分别作为第一,二,三关键字进行排序,那么如果对三元组从大到小进行遍历并用二维树状数组处理会有什么问题? 如果(a1,b1,c1)和(a2,b2,c2)满足 b1==b2&&c1==c2,那么我们无法知道是否 a1==a2。于是我们只要先把重复的叠起来,再这样做就可以了!!

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <stack>
#include <vector>
#include <cstring>
#include <queue>
#define msc(X) memset(X,-1,sizeof(X))
#define ms(X) memset(X,0,sizeof(X))
typedef long long LL;
using namespace std;

const int MAXN=1e5+5;
int mx[MAXN],cnt[MAXN];
struct _ABC
{
    int a,b,c,t;
    bool operator < (const struct _ABC &k) const{
        if(a!=k.a) return a>k.a;
        else if(b!=k.b) return b>k.b;
        else return c>k.c;
    }
}abc[MAXN];
int BIT2d[1003][1003];
void add(int x,int y)
{
    while(x<1001)
    {
        for(int i=y;i<1001;i+=i&-i)
            BIT2d[x][i]+=1;
        x+=x&-x;
    }
}
int sum(int x,int y)
{
    int rt=0;
    while(x)
    {
        for(int i=y;i;i-=i&-i)
            rt+=BIT2d[x][i];
        x-=x&-x;
    }
    return rt;
}
int main(void)
{
    int t,ti=0;
    scanf("%d",&t);
    while(++ti<=t)
    {
        int n,m;LL ans=0;
        scanf("%d%d",&n,&m);
        ms(mx);ms(cnt);
        for(int i=0;i<n;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            if(mx[b]<a) mx[b]=a,cnt[b]=1;
            else if(mx[b]==a) ++cnt[b];
        }
        n=0;
        for(int i=0;i<m;i++)
        {
            int c,d,e;
            scanf("%d%d%d",&c,&d,&e);
            if(mx[e])
                abc[n].a=mx[e],abc[n].b=c,abc[n].c=d,
                abc[n++].t=cnt[e];
        }
        sort(abc,abc+n);
        ms(BIT2d);
        m=1;
        for(int i=1;i<n;i++)
            if(abc[i].a==abc[m-1].a&&abc[i].b==abc[m-1].b&&abc[i].c==abc[m-1].c)
                abc[m-1].t+=abc[i].t;
            else abc[m++]=abc[i];
        for(int i=0;i<m;i++)
        {
            if(sum(1000,1000)-sum(abc[i].b-1,1000)
                -sum(1000,abc[i].c-1)+sum(abc[i].b-1,abc[i].c-1)==0)
                ans+=abc[i].t;
            add(abc[i].b,abc[i].c);
        }
        printf("Case #%d: %I64d\n",ti,ans );
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值