Codeforces 870E Points, Lines and Ready-made Titles

题目传送门

  传送门I

  传送门II

  传送门III

题目大意

  平面上有$n$个点,第$i$个点可以画一条平行于$y$轴且经过这个点的直线或者平行于$x$轴且经过这个点的直线或者什么都不做,问能够产生多少种本质不同的图案。

  考虑每个点与它$x$坐标相同且$y$坐标比它大的第一个点连一条无向边,以及与它$y$坐标相同且$x$坐标第一个比它大的点连一条无向边。

  显然,每个连通块和连通块之间互不影响,所以分别考虑每个连通块。

  设当前连通块所有点的横坐标构成的集合为$X$,纵坐标构成的集合为$Y$。

  • 如果点数等于边数减一,只有对于包含所有满足$x = x_i\vee y = y_i , x_i \in X, y_i \in Y$的直线的图案无法画出,其他图案都能画出。(这里的所有图案指,图案中包含的任意一条直线一定经过这中间某个点的所有图案)
    我们考虑用归纳法来证明它。假设新增的点的坐标是$(x_0, y_0)$
    • 当$|V| = 1$的时候显然成立。
    • 假设当$|V| = k - 1$的时成立,考虑加入一个叶节点。
      显然我们只需要证明直线数为$|X| + |Y| - 1$的所有图案都能画出。(其它的只需要选一个这样的图案,然后点一些点不操作就行了)
      • 假设新增加了一个横坐标$x_0$,那么考虑我们点当前这个点,画一条直线$x = x_0$,这样所有不存在的直线不是$x = x_0$的图案都可以画出了。

        (其中蓝色虚线表示不存在的直线,紫色表示前$k - 1$个点选择的直线,绿色表示第$k$个点选择的直线,红点是第$k$个点)

      • 考虑如果不存在直线$x = x_0$的图案,那么对于前$k - 1$个点,我们找一个不存在直线$y = y_0$的方案,然后过这个点画一条直线$y = y_0$。
  • 对于点数大于等于边数,我们考虑它的任意一个生成基环树。
    任意删去环上的一个点,剩下一堆树,根据以上结论,我们可以构造出一个包含所有满足$x = x_i\vee y = y_i , x_i \in X, y_i \in Y$的图案(先让树上某个直线画不了,然后这个新点去补锅,剩下的树可以用归纳法,不断添加叶节点来证明)。

  所以当一个连通块是树的时候方案数为$2^{|X| + |Y|} - 1$,否则是$2^{|X| + |Y|}$。

Code

  1 /**
  2  * Codeforces
  3  * Problem#870E
  4  * Accepted
  5  * Time: 93ms
  6  * Memory: 9800k
  7  */
  8 #include <algorithm>
  9 #include <iostream>
 10 #include <cstdlib>
 11 #include <cstdio>
 12 #include <set>
 13 using namespace std;
 14 typedef bool boolean;
 15
 16 template <typename T>
 17 void pcopy(T* pst, const T* ped, T* pval) {
 18     for ( ; pst != ped; *(pst++) = *(pval++));
 19 }
 20
 21 typedef class Point {
 22     public:
 23         int x, y, id;
 24
 25         Point(int x = 0, int y = 0, int id = 0):x(x), y(y), id(id) {}
 26 }Point;
 27
 28 typedef class union_found {
 29     public:
 30         int n;
 31         int *f;
 32         int *dif;    // |E| - |V|
 33
 34         union_found() {    }
 35         union_found(int n):n(n) {
 36             f = new int[(n + 1)];
 37             dif = new int[(n + 1)];
 38             for (int i = 1; i <= n; i++)
 39                 f[i] = i, dif[i] = -1;
 40         }
 41
 42         int find(int x) {
 43             return (f[x] == x) ? (x) : (f[x] = find(f[x]));
 44         }
 45
 46         void unit(int u, int v) {
 47             int fu = find(u), fv = find(v);
 48             if (fu == fv) {
 49                 dif[fu]++;
 50                 return;
 51             }
 52             dif[fu] += dif[fv] + 1;
 53             f[fv] = fu;
 54         }
 55
 56         int operator [] (int u) {
 57             return find(u);
 58         }
 59
 60         int operator () (int u) {
 61             return dif[u];
 62         }
 63 }union_found;
 64
 65 const int M = 1e9 + 7;
 66
 67 int add(int a, int b) {
 68     return ((a += b) >= M) ? (a - M) : (a);
 69 }
 70
 71 int mul(int a, int b) {
 72     return a * 1ll * b % M;
 73 }
 74
 75 int sub(int a, int b) {
 76     return ((a -= b) < 0) ? (a + M) : (a);
 77 }
 78
 79 int qpow(int a, int p) {
 80     int pa = a, rt = 1;
 81     for ( ; p; p >>= 1, pa = mul(pa, pa))
 82         if (p & 1)
 83             rt = mul(rt, pa);
 84     return rt;
 85 }
 86
 87 int n;
 88 Point* ps;
 89 set<int> *sx, *sy;
 90 union_found dsu;
 91
 92 inline void init() {
 93     scanf("%d", &n);
 94     ps = new Point[(n + 1)];
 95     for (int i = 1, x, y; i <= n; i++) {
 96         scanf("%d%d", &x, &y);
 97         ps[i] = Point(x, y, i);
 98     }
 99 }
100
101 inline void solve() {
102     dsu = union_found(n);
103     sort(ps + 1, ps + n + 1, [&] (const Point& a, const Point& b) {    return (a.x ^ b.x) ? (a.x < b.x) : (a.y < b.y);    });
104     for (int i = 1, j, x; i <= n; i = j) {
105         for (x = ps[i].x, j = i + 1; j <= n && ps[j].x == x; j++);
106         for (int k = i; k < j; k++)
107             if (k + 1 < j)
108                 dsu.unit(ps[k].id, ps[k + 1].id);
109     }
110
111     for (int i = 1; i <= n; i++)
112         swap(ps[i].x, ps[i].y);
113     sort(ps + 1, ps + n + 1, [&] (const Point& a, const Point& b) {    return (a.x ^ b.x) ? (a.x < b.x) : (a.y < b.y);    });
114     for (int i = 1, j, x; i <= n; i = j) {
115         for (x = ps[i].x, j = i + 1; j <= n && ps[j].x == x; j++);
116         for (int k = i; k < j; k++)
117             if (k + 1 < j)
118                 dsu.unit(ps[k].id, ps[k + 1].id);
119     }
120
121     sx = new set<int>[(n + 1)];
122     sy = new set<int>[(n + 1)];
123     for (int i = 1, p; i <= n; i++) {
124          p = ps[i].id;
125          sx[dsu[p]].insert(ps[i].x);
126          sy[dsu[p]].insert(ps[i].y);
127     }
128     int res = 1;
129     for (int i = 1; i <= n; i++) {
130         if (dsu[i] == i)
131             res = mul(res, sub(qpow(2, sx[i].size() + sy[i].size()), (dsu(i) < 0)));
132     }
133     printf("%d\n", res);
134 }
135
136 int main() {
137     init();
138     solve();
139     return 0;
140 }

原文地址:https://www.cnblogs.com/yyf0309/p/9873974.html

时间: 2024-10-08 19:28:39

Codeforces 870E Points, Lines and Ready-made Titles的相关文章

Codeforces 870E Points, Lines and Ready-made Titles:并查集【两个属性二选一】

题目链接:http://codeforces.com/problemset/problem/870/E 题意: 给出平面坐标系上的n个点. 对于每个点,你可以画一条经过这个点的横线或竖线或什么都不画. 两条重合的直线算作一条直线. 问你能画出多少种不同的图案. 题解: 将所有横坐标或纵坐标相同的两点之间连边. 对于一个连通块,设这个连通块中不同的横坐标个数为sx,不同的纵坐标个数为sy. 有可能画出的线的个数即为sx + sy. 可以发现,如果一个联通块中有环(即siz[fa] >= sx +

Codeforces 19D Points 线段树+set

题目链接:点击打开链接 线段树维护y值大于val的最小x值 #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <iostream> #include <map> #include <set> #include <math.h> using namespace std; #define inf

Codeforces 19D Points(树状数组)

题目链接:Codeforces 19D Points 题目大意:N中操作,每次添加一个点,或者删除一个点,以及找到给定x,y坐标最近的一个坐标,并且保证xi,yi在x,y的右上角. 解题思路:这题的解法还是很机智的. y坐标离散化,然后树状数组的每个单位用一个set代替,set记录的是点集. 剩下的操作就像树状数组一样,每次添加就等于是+w的操作,移除就等于是-w,只是w是一个点,那么find操作就等于是在sum操作生成的点集中二分查找. #include <cstdio> #include

Codeforces 430A Points and Segments (easy)

题意:让你染色点,要求在给出的区间内|红色个数-蓝色个数|<=1 思路:排序后依次交替染色就能达到效果 #include <iostream> #include <cstring> #include <algorithm> #include <cstdio> #include <vector> using namespace std; const int MAXN = 110; int arr[MAXN]; int n,m,x,y; int

CodeForces A. Points in Segments

http://codeforces.com/contest/1015/problem/A You are given a set of nn segments on the axis OxOx, each segment has integer endpoints between 11 and mm inclusive. Segments may intersect, overlap or even coincide with each other. Each segment is charac

CodeForces 251A. Points on Line(数学 lower_bound )

题目链接:http://codeforces.com/problemset/problem/251/A Little Petya likes points a lot. Recently his mom has presented him n points lying on the line OX. Now Petya is wondering in how many ways he can choose three distinct points so that the distance be

CodeForces 19D Points(离散化+线段树+单点更新)

题目链接: huangjing 题意:给了三种操作 1:add(x,y)将这个点加入二维坐标系 2:remove(x,y)将这个点从二维坐标系移除. 3:find(x,y)就是找到在(x,y)右上方的第一个点. 思路:我们可以建立n个set以x为横坐标,那么我们这个题就转化为找一个最小的x是否存在满足条件,那么x一旦被找到,那么纵坐标就自然而然的找到了,当然更新操作就是对maxy的维护,然后查询操作就是找出一个最小的x..还有因为n非常大,所以要采用离散化的方法,然后进行离线处理.还是就是掌握s

codeforces 251A Points on Line(二分or单调队列)

Description Little Petya likes points a lot. Recently his mom has presented him n points lying on the line OX. Now Petya is wondering in how many ways he can choose three distinct points so that the distance between the two farthest of them doesn't e

[CodeForces] Moving Points

Problem Statement Link Clarification: The problem states that for each pair of points, we consider the minimum possible distance over any possible moments of time, including non-integer time interval. This means each different pair is independent, an