【YBT2023寒假Day6 A】寄蒜挤河(计算几何)(线段树)

寄蒜挤河

题目链接:YBT2023寒假Day6 A

题目大意

有一些二维平面上的点,按顺序加入,每次加入一个点后,问你图上有多少个三元点对使得它们构成的三角形严格包含原点,就是原点不能在点上或边上。

思路

发现直接按着求很难搞,似乎只能做到 \(n^2\) 之类的。
考虑转化一下,求不合法的方案数。

会发现其实不合法的条件是三个点跨越的极角不超过 \(180^\circ\)
(以原点在直线上的时候是 \(180^\circ\) 为最大)

考虑每次插入一个 \(x\) 时候不合法方案的增加数。
考虑分别找到 \(x\) 顺时针逆时针 \(180^\circ\) 范围极角差最大的点 \(y,z\)
那一个方法就是枚举 \(y\sim x\) 里面的一个点 \(u\),然后另两个(这三个其中一个包括 \(x\))在 \(u\) 逆时针 \(180^\circ\) 选。

分类讨论一下,如果 \(u\neq x\),那我们设 \(f_x\)\(x\) 逆时针 \(180^\circ\) 里面点的个数(不算 \(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;
} 
posted @ 2023-02-07 19:27  あおいSakura  阅读(10)  评论(0编辑  收藏  举报