hdu5517 Triple

37 篇文章 0 订阅
15 篇文章 0 订阅

题目链接

题意有一点点绕…但最终就是找三维坐标内满足没有点的三个坐标都大于此点的点的数量。
首先先按输入的b,e分组。然后枚举不同的b,一个小trick就是同组中,将c,d放到二维坐标中必然右上角是没点的时候,这个点才能通过选本组中最大的a来使得满足条件。
这些点可以通过bit来check,注意c,d相同的点要放在一起check。
用pair数组 d [ c ] [ d ] d[c][d] d[c][d]来表示最大的a,和数量。
不同的组中,相同的c,d应该取最大的a。
最后搞完之后,就枚举c,d来累计答案。但是还有一些点是可能不合法的,就是存在
c ′ > c ∣ ∣ d ′ > d    & &    d [ c ′ ] [ d ′ ] . a > d [ c ] [ d ] . a c'>c|| d'>d~~ \&\& ~~d[c'][d'].a>d[c][d].a c>cd>d  &&  d[c][d].a>d[c][d].a的情况时,c,d是不能合法的。这时候搞一个数组记录一下后缀的最大值即可。
大致思路就是这样,有一些细节见代码:

#include <bits/stdc++.h>
using namespace std;
const int N =1e5+3;
#define pb push_back
#define fi first
#define se second
#define LL long long
int t,cas,n,m;
pair<int,LL>d[1003][1003];
int mx[1003][1003];
vector<int>v[N];
vector<pair<int,int>>g[N];
int tr[1003];
void add(int x,int y){
  for(;x;x-=x&-x)tr[x]+=y;
}
int get(int x){
  int y=0;
  for(;x<=1000;x+=x&-x)y+=tr[x];
  return y;
}
int main(){
  for(scanf("%d",&t),cas=1;cas<=t;cas++){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=100000;i++)v[i].clear(),g[i].clear();
    for(int i=1;i<=1000;i++){
      for(int j=1;j<=1000;j++){
        d[i][j]={0,0};
      }
    }
    for(int i=1;i<=n;i++){
      int l,r;
      scanf("%d%d",&l,&r);
      v[r].pb(l);
    }
    for(int i=1;i<=m;i++){
      int x,y,z;
      scanf("%d%d%d",&x,&y,&z);
      g[z].pb({x,y});
    }
    for(int i=1;i<=100000;i++){
      sort(v[i].begin(),v[i].end(),[](int x,int y){return x>y;});
      sort(g[i].begin(),g[i].end(),[](pair<int,int> x,pair<int,int> y){
        if(x.fi==y.fi)return x.se>y.se;
        return x.fi>y.fi;
      });
      int l=v[i].size(),r=g[i].size();
      if(!l ||!r)continue;
      int cnt=0,mx=v[i][0];
      for(auto k:v[i]){
        if(k==v[i][0])++cnt;
      }
      int inx=0;
      for(int j;inx<r;inx=j+1){
        j=inx;
        while(j+1<r&&g[i][j+1].fi==g[i][j].fi&&g[i][j+1].se==g[i][j].se)j++;
        for(int k=inx;k<=j;k++){
          auto x=g[i][k];
          if(!get(x.se)){
            if(d[x.fi][x.se].fi<mx){
              d[x.fi][x.se]={mx,cnt};
            }else if(d[x.fi][x.se].fi==mx){
              d[x.fi][x.se].se+=cnt;
            }
          }
        }
        for(int k=inx;k<=j;k++)add(g[i][k].se,1);
      }
      for(int j=0;j<r;j++){
        int x=g[i][j].se,y=g[i][j].fi;
        add(x,-1);
      }
    }
    LL ans=0;
    for(int i=1000;i>=1;i--){
      for(int j=1000;j>=1;j--){
        mx[i][j]=max({mx[i+1][j],mx[i][j+1],d[i][j].fi});
      }
    }
    for(int i=1;i<=1000;i++){
      for(int j=1;j<=1000;j++){
        if(d[i][j].fi>max(mx[i][j+1],mx[i+1][j]))ans+=d[i][j].se;
      }
    }
    printf("Case #%d: %lld\n",cas,ans);
  }
  return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值