牛客暑假多校第五场 I vcd

这个题目一个队友没读懂, 然后我读错了题目, 还让他堆了半天的公式写了半天的代码, 交上去一直0.0, 另一队友问题目有没有读错, 我坚持没有读错, 然后坑了2个小时的时间,不然应该会早一点做出来。

题意: 平面上有n个不重和的点, 现在定义一个集合是好的, 那么就需要满足, 这个集合里的每一个子集 都存在一个 {(x, y) |x >= a,  l <= y <= r}(a, l, r为任意实数) 的这个区域与原来集合求交 之后能表示出来。求这个n个点所组成的集合的非空子集有多少个是合法的。

解释一下样例的话 就是 对于 { (1,1) (2,2) (3,3) } 这个集合来说他不是好的一个好的集合, 他的子集{(1,1)}   {(2,2)}   {(3,3)}  {(1,1) (2,2)}  {(2,2) (3,3)}  {(1,1) (2,2) (3,3)}  都可以通过原集合 和 一个{(x, y) |x >= a,   l <= y <= r} 求交集得到, 但是对于 {(1,1) (3,3)} 这个子集来说, 不能求交集得到, 因为如果 存在(1,1) (3,3)  那么也一定存在 (2,2)。

题解:

只有一个点的情况 都是合法的

有2个点的情况 除了同y轴的任意2点组成的集合也是合法的

有3个点的情况 我们发现只有 一个点的 x 坐标小于 其他2个点的x坐标,并且 另外2个点的y坐标一个大于 这个点的y坐标 另一个小于 这个点的y坐标 这样的情况才会合法。

3个点以上的情况都是非法的。

我们每次处理到一个点 (x, y) 之后,我们都查询满足 (xi > x, yi > y)点的个数 设他为a, 然后查询(xi > x, yi < y) 的点的个数 设他为b, 

先这个点与其他的点组个成2个点的情况 ans += a + b。

其次这个点与其他的点组成3个点的情况 ans += a * b。

这样处理完所有的点之后我们就可以求解了。

我们用树状数组来加快查询的速度。

首先存下所有的点, 离散化y的坐标, 然后在树状数组相应的位置加上值。

再从左到右处理所有的点, 每次处理到一个点的时候, 我们要先把所有的同x轴的点的值先删除掉, 因为同x轴的点只能和前面的点组成3个点的形式,但是这个东西在前面的点就处理过了,并且我们需要查询的是 后面的点的不同 y 的数目, 所以要先删除这些点的影响, 但是这些点都是可以两两组合的, 这个要先加上去。

代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout);
 4 #define LL long long
 5 #define ULL unsigned LL
 6 #define fi first
 7 #define se second
 8 #define pb push_back
 9 #define lson l,m,rt<<1
10 #define rson m+1,r,rt<<1|1
11 #define max3(a,b,c) max(a,max(b,c))
12 #define min3(a,b,c) min(a,min(b,c))
13 typedef pair<int,int> pll;
14 const int inf = 0x3f3f3f3f;
15 const LL INF = 0x3f3f3f3f3f3f3f3f;
16 const LL mod = 998244353;
17 const int N = 1e5 + 100;
18 pll p[N];
19 pll pp[N];
20 int vis[N];
21 int tree[N];
22 int k = 0;
23 void add(int x, int c){
24     while(x <= k){
25         tree[x]+=c;
26         x += x&(-x);
27     }
28 }
29 int query(int x){
30     int ret = 0;
31     while(x){
32         ret += tree[x];
33         x -= x&(-x);
34     }
35     return ret;
36 }
37 int main(){
38     int n;
39     scanf("%d", &n);
40     for(int i = 1; i <= n; i++){
41         scanf("%d%d", &p[i].fi, &p[i].se);
42         pp[i].se = i;
43         pp[i].fi = p[i].se;
44     }
45     sort(pp+1, pp+1+n);
46     for(int i = 1; i <= n; i++){
47         if(pp[i].fi != pp[i-1].fi) k++;
48         p[pp[i].se].se = k;
49     }
50     for(int i = 1; i <= n; i++)
51         add(p[i].se, 1);
52     sort(p+1, p+n+1);
53     LL ans = n;
54     for(int i = 1; i <= n; i++){
55         pp[0] = p[i];
56         int j = i+1, tt = 1;
57         while(j <= n && p[j].fi == pp[0].fi)
58             pp[tt++] = p[j++];
59         i = j - 1;
60         for(int z = 0; z < tt; z++)
61             add(pp[z].se, -1);
62         ans += 1ll * tt * (tt-1) / 2;
63         for(int z = 0; z < tt; z++){
64             int y = pp[z].se;
65             LL t1 = query(k) - query(y);
66             LL t2 = query(y-1);
67             ans = ans + t1 * t2 + t1 + t2;
68             ans %= mod;
69         }
70     }
71     cout << ans << endl;
72     return 0;
73 }
View Code
原文地址:https://www.cnblogs.com/MingSD/p/9414041.html