寄蒜挤河
题目链接:YBT2023寒假Day6 A
题目大意
有一些二维平面上的点,按顺序加入,每次加入一个点后,问你图上有多少个三元点对使得它们构成的三角形严格包含原点,就是原点不能在点上或边上。
思路
发现直接按着求很难搞,似乎只能做到 \(n^2\) 之类的。
考虑转化一下,求不合法的方案数。
会发现其实不合法的条件是三个点跨越的极角不超过 \(180\degree\)。
(以原点在直线上的时候是 \(180\degree\) 为最大)
考虑每次插入一个 \(x\) 时候不合法方案的增加数。
考虑分别找到 \(x\) 顺时针逆时针 \(180\degree\) 范围极角差最大的点 \(y,z\)。
那一个方法就是枚举 \(y\sim x\) 里面的一个点 \(u\),然后另两个(这三个其中一个包括 \(x\))在 \(u\) 逆时针 \(180\degree\) 选。
分类讨论一下,如果 \(u\neq x\),那我们设 \(f_x\) 为 \(x\) 逆时针 \(180\degree\) 里面点的个数(不算 \(x\) 自己),那就是选一个 \(x\) 再选一个,就是 \(f_u\)。
如果 \(u=x\),那就是在旁边再选两个,就是 \(x\) 到 \(z\) 之间点的个数(算 \(z\) 不算 \(x\))\(d\) 的 \(\binom{d}{2}\)。
然后我们考虑维护 \(f_x\),其实很简单,每次加入 \(x\) 的时候把 \(y\sim x\) 极角范围内不包括 \(x\) 的点 \(f\) 值加一即可。
不过要注意你打标记不一定要加上去,要离散点,、之前已经放进来的点你再加(或者用平衡树也行,因为好像说原本打算出的是强制在线)
代码
#include<map>
#include<set>
#include<cmath>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const ll N = 4e5 + 100;
const double pi = acos(-1.0);
const double eps = 1e-11;
struct dian {
ll x, y, id; double k;
}a[N], b[N];
ll n, dy[N];
double bk[N];
ll ans[N];
bool cmp(dian x, dian y) {
return x.k < y.k;
}
bool cmp1(double x, double y) {
return y - x > eps;
}
double Abs(double x) {return x < 0 ? -x : x;}
ll Abs(ll x) {return x < 0 ? -x : x;}
struct XD_tree {
ll f[N << 2], lzy[N << 2];
ll wkn[N << 2];
void up(ll now) {
f[now] = f[now << 1] + f[now << 1 | 1];
wkn[now] = wkn[now << 1] + wkn[now << 1 | 1];
}
void downa(ll now, ll l, ll r, ll x) {
lzy[now] += x;
f[now] += x * wkn[now];
}
void down(ll now, ll l, ll mid, ll r) {
if (lzy[now]) {
downa(now << 1, l, mid, lzy[now]); downa(now << 1 | 1, mid + 1, r, lzy[now]);
lzy[now] = 0;
}
}
void mark(ll now, ll l, ll r, ll pl, ll x) {
if (l == r) {
wkn[now] = 1; f[now] = x;
return ;
}
ll mid = (l + r) >> 1; down(now, l, mid, r);
if (pl <= mid) mark(now << 1, l, mid, pl, x);
else mark(now << 1 | 1, mid + 1, r, pl, x);
up(now);
}
void update(ll now, ll l, ll r, ll L, ll R, ll x) {
if (L > R) return ;
if (L <= l && r <= R) {
downa(now, l, r, x); return ;
}
ll mid = (l + r) >> 1; down(now, l, mid, r);
if (L <= mid) update(now << 1, l, mid, L, R, x);
if (mid < R) update(now << 1 | 1, mid + 1, r, L, R, x);
up(now);
}
ll query(ll now, ll l, ll r, ll L, ll R) {
if (L > R) return 0ll;
if (L <= l && r <= R) return f[now];
ll mid = (l + r) >> 1; down(now, l, mid, r); ll re = 0;
if (L <= mid) re += query(now << 1, l, mid, L, R);
if (mid < R) re += query(now << 1 | 1, mid + 1, r, L, R);
return re;
}
ll queryn(ll now, ll l, ll r, ll L, ll R) {
if (L > R) return 0;
if (L <= l && r <= R) return wkn[now];
ll mid = (l + r) >> 1; down(now, l, mid, r); ll re = 0;
if (L <= mid) re += queryn(now << 1, l, mid, L, R);
if (mid < R) re += queryn(now << 1 | 1, mid + 1, r, L, R);
return re;
}
}T;
int main() {
freopen("geometry.in", "r", stdin);
freopen("geometry.out", "w", stdout);
scanf("%lld", &n);
for (ll i = 1; i <= n; i++) {
scanf("%lld %lld", &a[i].x, &a[i].y); a[i].k = atan2(a[i].y, a[i].x); a[i].id = i; b[i] = a[i];
}
sort(b + 1, b + n + 1, cmp);
for (ll i = 1; i <= n; i++) bk[i] = b[i].k;
for (ll i = 1; i <= n; i++) dy[b[i].id] = i;
ll ans = 0;
for (ll i = 1; i <= n; i++) {
ll now = dy[i];
double k2 = a[i].k - pi; if (k2 <= -pi) k2 += 2 * pi;
ll x = lower_bound(bk + 1, bk + n + 1, k2, cmp1) - bk;
ll l = x, r = now - 1; if (l == n + 1) l = 1; if (r == 0) r = n;
if (now != x) {
if (l <= r) ans += T.query(1, 1, n, l, r), T.update(1, 1, n, l, r, 1);
else ans += T.query(1, 1, n, l, n) + T.query(1, 1, n, 1, r), T.update(1, 1, n, l, n, 1), T.update(1, 1, n, 1, r, 1);
}
k2 = a[i].k + pi; if (k2 >= pi) k2 -= 2 * pi;
x = upper_bound(bk + 1, bk + n + 1, k2, cmp1) - bk - 1;
l = now + 1, r = x; if (l == n + 1) l = 1; if (r == 0) r = n; ll num = 0;
if (now != x) {
if (l <= r) num = T.queryn(1, 1, n, l, r);
else num = T.queryn(1, 1, n, l, n) + T.queryn(1, 1, n, 1, r);
}
ans += 1ll * num * (num - 1) / 2;
T.mark(1, 1, n, now, num);
printf("%lld\n", 1ll * i * (i - 1) * (i - 2) / 6 - ans);
}
return 0;
}
标签:include,return,degree,Day6,double,ll,YBT2023,寄蒜,now
From: https://www.cnblogs.com/Sakura-TJH/p/YBT2023Day6_A.html