【upc】 2020年秋季组队训练赛第十七场 I Check List | 线段树

When test driving a vehicle, you are asked to make a checkmark on a tablet.  Upon inspection, you have noticed many fingerprints on the screen.  Aside from the rush of disgust, you notice that the fingerprints on the screen can be represented by a series of 2D points on a standard Cartesian grid.  You know that checkmarks can be uniquely defined by three points; these three points have distinct x coordinates and the  three  points  also  have  distinct  y  coordinates.    The  leftmost  point  must  be  lower  than  the rightmost  point and higher than the middle point.  Now you want  to know how many unique checkmarks you can make using the 2D points. 
 
Let’s  consider  some  examples.   The  three  points  (1,2),  (2,1),  and  (3,2)  do  not  form  a  valid checkmark because the leftmost and rightmost points are at the same height.  The three points (1,2),  (2,1),  and  (2,3)  do  not  form  a  valid  checkmark  because  two  points  have  the  same  x coordinates.  The three points (1,2), (3,1), and (4,9) do form a valid checkmark. 
 
Given a list of 2D points, determine the number of unique checkmarks that can be formed from them. 

输入

The first input line contains a single integer, n (1 ≤ n ≤ 100,000), representing the number of points.   Each  of  the  following  n  input  lines  contains  two  integers,  xi   and  yi   (1  ≤  xi ,  yi   ≤ 
1,000,000,000), representing the location of a point on the 2D grid.  No two points will be identical. 

输出

Print the number of distinct checkmarks that can be formed by the given 2D points. 

样例输入 Copy

【样例1】
6 
6 6 
5 3 
1 5 
3 2 
4 4 
2 1 
【样例2】
10 
4 2 
9 4 
1 5 
2 4 
10 5 
6 1 
3 3 
8 3 
5 1 
7 2 

样例输出 Copy

【样例1】
5
【样例2】
20

题目大意:

你需要找出有多少个三元组(i,j,k)满足:

1.xi < xj < xk

2.hk > hi > hj

题目思路:

首先按照x排序之后,降维

之后,假设当前位置是pos

考虑当前位置作为第二个的贡献,即对所有之前出现过的数 大于h[pos]的+1

然后再加上pos作为第三个数的贡(即对答案的贡献)

所以你需要维护两个操作:

1.对所有出现过的数,在[x,y]区间内都+1

2.求所有出现过的数,在[x,y]区间内的权值

因为题目要求出现过,所以用线段树再次维护一个b权值,代表当前节点下,出现了多少个点

那么lazy 向下传 就不传 区间长度了 而是b[k]

每次单点修改时,将lazy传递下来,这样就不会影响新的lazy传递

Code:

#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef  long long ll;
typedef unsigned long long ull;
const ll INF= 1e16;
const int maxn = 1e6+7;
const int mod= 998244353;
inline bool read(ll &num)
{char in;bool IsN=false;
in=getchar();if(in==EOF) return false;while(in!='-'&&(in<'0'||in>'9')) in=getchar();if(in=='-'){ IsN=true;num=0;}else num=in-'0';while(in=getchar(),in>='0'&&in<='9'){num*=10,num+=in-'0';}if(IsN) num=-num;return true;}
ll n,m,p;
struct Node{
    int x,y;
    bool friend operator<(Node a,Node b){
        if(a.x == b.x) return a.y < b.y;
        return a.x < b.x;
    }
}q[maxn];
ll sum[maxn],lazy[maxn],b[maxn];
vector<int>v;
int getid(ll x){
    return lower_bound(v.begin(),v.end(),x) - v.begin() + 1;
}
void push(int k){
    b[k] = b[k<<1]+b[k<<1|1];
    sum[k] = sum[k<<1] + sum[k<<1|1];
}
void down(int k){
    if(lazy[k]){
        sum[k<<1] += lazy[k]*b[k<<1];
        sum[k<<1|1] += lazy[k]*b[k<<1|1];
        lazy[k<<1] += lazy[k];
        lazy[k<<1|1] += lazy[k];
        lazy[k] = 0;
    }
}
void ModifyOne(int k,int l,int r,int pos){
    if(l == r){
        b[k]++;
        return;
    }
    down(k);
    int mid = (l+r)/2;
    if(pos<=mid) ModifyOne(k<<1,l,mid,pos);
    else ModifyOne(k<<1|1,mid+1,r,pos);
    push(k);
}
void Modify(int k,int l,int r,int x,int y){
    if(x<=l&&y>=r){
        sum[k] += b[k];
        lazy[k] ++;
        return;
    }
    down(k);
    int mid = (l+r)/2;
    if(x<=mid) Modify(k<<1,l,mid,x,y);
    if(y>mid) Modify(k<<1|1,mid+1,r,x,y);
    push(k);
}
ll Query(int k,int l,int r,int x,int y){
    if(x<=l&&y>=r) return sum[k];
    down(k);
    int mid = (l+r)/2;
    ll ans = 0;
    if(x<=mid) ans += Query(k<<1,l,mid,x,y);
    if(y>mid) ans += Query(k<<1|1,mid+1,r,x,y);
    push(k);
    return ans;
}
int main(){
    read(n);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&q[i].x,&q[i].y);
        v.push_back(q[i].y);
    }
    sort(q+1,q+1+n);
    sort(v.begin(),v.end());
    v.erase(unique(v.begin(),v.end()),v.end());
    int nn = v.size();
    unsigned long long ans = 0;
    for(int i=1;i<=n;i++){
      //  printf("%d %d\n",q[i].x,q[i].y);
        int k = i;
        while(k<=n&&q[k].x == q[i].x){
            int id = getid(q[k].y);
            if(id > 2) ans += Query(1,1,nn,1,id-1);
            k++;
        }
        k = i;
        while(k<=n&&q[k].x == q[i].x){
            int id = getid(q[k].y);
            Modify(1,1,nn,id+1,nn);
            k++;
        }
        k = i;
        while(k<=n&&q[k].x == q[i].x){
            int id = getid(q[k].y);
            ModifyOne(1,1,nn,id);
            k++;
        }
        i = k - 1;
    }
    printf("%llu\n",ans);
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只酷酷光儿( CoolGuang)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值