牛客暑假多校第五场 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 }