题解 想不出
没看见“有限区域”爆零了
首先这个 30 度很烦,我们转一下坐标系
就是原来的基向量乘上一个矩阵等于新的基向量,解出这个矩阵来即可
现在每个点可以向右/上引出一条射线,求最大有限区域数
考虑用欧拉公式转化一下
发现对于最终的每个连通块,每个有限区域的每条边唯一对应一条射线
那么我们要做的就是最大化这些射线的交点数
发现下图这样的东西一定不优
|
——
所以
那么可以枚举每个点为其定向
定向依据是其左上和右下的点数哪个多
最后欧拉公式算面数
于是可以做到 \(O(n\log n)\)
注意图中可以有不止一个连通块
hack 数据:
2
1 1
1 100
但我懒得处理了
所以只能做到 \(O(n^2)\) 吧,还不算并查集的复杂度
被沈老师 D 了
发现有连边的点形成二维偏序
所以一个思路是第一维用主席树,每个叶子节点再开一个线段树优化建图
这样是 \(O(n\log n)\) 的,但常数较大
于是又有一个常数更小的 \(O(n\log n)\) 做法是分治
还是处理二维偏序:对横坐标分治
将左右两边分别排序,若 \(y_{r_i}\leqslant y_{l_k}\) 且 \(y_{r_j}\leqslant y_{l_k}\),则并查集合并 \(r_i, r_j\) 就可以了
复杂度 \(T(n)=O(n)+2T(\frac{n}{2})=O(n\log n)\),排序可以提前排好
一个有趣的问题:如果求所有区域(不要求有限)该怎么做呢?
答案是最大交点数 +1
考虑不旋转坐标系时,每个交点一定是一个区域内纵坐标最大的点
所以一个交点唯一对应一个平面
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define fir first
#define sec second
#define ll long long
// #define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n;
pair<int, int> p[N];
namespace task{
#undef unix
bool up[N];
int sizx, sizy, m;
double unix[N], uniy[N];
pair<double, double> q[N];
const double tr[2][2]={-0.5, 0.5,-sqrt(3)/6, -sqrt(3)/6};
struct BIT1{
int a[N];
inline void upd(int i, int dat) {for (; i; i-=i&-i) a[i]+=dat;}
inline int query(int i) {int ans=0; for (; i<=sizy; i+=i&-i) ans+=a[i]; return ans;}
void clear() {memset(a, 0, sizeof(a));}
}bit1;
struct BIT2{
int a[N];
inline void upd(int i, int dat) {for (; i<=sizy; i+=i&-i) a[i]+=dat;}
inline int query(int i) {int ans=0; for (; i; i-=i&-i) ans+=a[i]; return ans;}
void clear() {memset(a, 0, sizeof(a));}
}bit2;
void solve() {
for (int i=1; i<=n; ++i) {
unix[++sizx]=q[i].fir=p[i].fir*tr[0][0]+p[i].sec*tr[1][0];
uniy[++sizy]=q[i].sec=p[i].fir*tr[0][1]+p[i].sec*tr[1][1];
}
sort(unix+1, unix+sizx+1); sort(uniy+1, uniy+sizy+1);
sizx=unique(unix+1, unix+sizx+1)-unix-1;
sizy=unique(uniy+1, uniy+sizy+1)-uniy-1;
for (int i=1; i<=n; ++i) p[i]={lower_bound(unix+1, unix+sizx+1, q[i].fir)-unix, lower_bound(uniy+1, uniy+sizy+1, q[i].sec)-uniy};
sort(p+1, p+n+1);
for (int i=1; i<=n; ++i) bit2.upd(p[i].sec, 1);
for (int i=1; i<=n; ++i) {
bit2.upd(p[i].sec, -1);
if (bit1.query(p[i].sec)>=bit2.query(p[i].sec)) up[i]=1;
bit1.upd(p[i].sec, 1);
}
bit1.clear();
for (int i=1; i<=n; ++i)
if (up[i]) m+=bit1.query(p[i].sec);
else bit1.upd(p[i].sec, 1);
cout<<(m-n+1)<<endl;
}
}
signed main()
{
freopen("surface.in", "r", stdin);
freopen("surface.out", "w", stdout);
n=read();
for (int i=1; i<=n; ++i) p[i].fir=read(), p[i].sec=read();
task::solve();
return 0;
}