题解 [ICPC2021 昆明] Helesta

传送门
传送门(暂时无法评测,什么时候滥用一下 admin 账号)

很神仙的题

发现两个相交的半平面的异或两个相对的扇形
联想到若所有半平面交于一点,
则将这些半平面绕这个点排序后临项的异或和之和恰为整个平面
但是它们并不一定交于一点怎么办呢?
神仙的地方来了:
考虑平面内随机撒 \(B\) 个关键点
令每个半平面归属于离它最近的关键点

  • \((x, y)\) 到半平面 \(Ax+By+c\) 的距离为 \(Ax+By+c\),写在这里是因为我发现我完全不理解半平面的那个定义是啥

然后对每个关键点做旋转扫描线
考虑这样产生的误差就是直线平移过程中经过的点数
而这个点数是 \(O(B)\) 级别的
所以期望复杂度 \(O(m\sqrt m)\)
Accoders 上评不了,牛客网上过了

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f3f3f3f3f
#define N 200010
#define fir first
#define sec second
#define pb push_back
#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, m;
pair<ll, ll> p[N];
ll a[N], b[N], c[N];

namespace task1{
	int sta[N], top;
	void solve() {
		random_device seed;
		mt19937 rand(seed());
		for (int i=1; i<=m; ++i) sta[++top]=i;
		shuffle(sta+1, sta+top+1, rand);
		for (int i=1; i<=m; ++i) printf("%d\n", sta[i]);
	}
}

namespace task{
	vector<int> sta[N];
	pair<ll, int> tem[N];
	int rk[N], lim=1e6, sqr, top;
	struct point{
		ll x, y;
		inline ll operator * (point p) {return x*p.y-y*p.x;}
	}a[N], center;
	struct plain{
		ll a, b, c, id, bel;
		inline ll operator * (point p) {return a*p.x+b*p.y+c;}
		inline ll operator * (plain p) {return a*p.b-b*p.a;}
	}b[N];
	void solve() {
		random_device seed;
		mt19937 rand(seed());
		center={rand()%(lim<<1)-lim, rand()%(lim<<1)-lim};
		sqr=min(n, (int)sqrt(m)+1);
		for (int i=1; i<=n; ++i) a[i]={p[i].fir, p[i].sec};
		for (int i=1; i<=m; ++i) b[i]={::a[i], ::b[i], ::c[i], i, 0};
		shuffle(a+1, a+n+1, rand);
		for (int i=1; i<=m; ++i) {
			ll minn=INF; int mini=1;
			for (int j=1; j<=sqr; ++j) {
				ll tem=b[i]*a[j];
				if (tem>=0 && tem<minn) minn=tem, mini=j;
			}
			sta[mini].pb(i);
			// b[i].bel=mini;
		}
		for (int i=1; i<=sqr; ++i)
			sort(sta[i].begin(), sta[i].end(), [&](int u, int v){
				ll t1=b[u]*a[i], t2=b[v]*a[i];
				if ((t1>0)!=(t2>0)) return (t1>0)<(t2>0);
				else return b[u]*b[v]<0;
			});
		for (int i=1; i<=sqr; ++i) tem[i]={a[i].x*center.x+a[i].y*center.y, i};
		sort(tem+1, tem+sqr+1);
		for (int i=1; i<=sqr; ++i) for (auto it:sta[tem[i].sec]) printf("%d\n", it);
		// for (int i=1; i<=sqr; ++i) tem[i]={a[i]*center, i};
		// sort(tem+1, tem+sqr+1);
		// for (int i=1; i<=sqr; ++i) rk[tem[i].sec]=i;
		// rk[sqr+1]=sqr+1;
		// sort(b+1, b+m+1, [](plain a, plain b){
		// 	if (a.bel!=b.bel) return rk[a.bel]<rk[b.bel];
		// 	ll t1=a*center, t2=b*center;
		// 	if ((t1>0)!=(t2>0)) return (t1>0)<(t2>0);
		// 	else return a*b<0;
		// });
		// for (int i=1; i<=m; ++i) printf("%d\n", b[i].id);
	}
}

signed main()
{
	freopen("c.in", "r", stdin);
	freopen("c.out", "w", stdout);

	n=read(); m=read();
	for (int i=1; i<=n; ++i) p[i].fir=read(), p[i].sec=read();
	for (int i=1; i<=m; ++i) a[i]=read(), b[i]=read(), c[i]=read();
	task::solve();

	return 0;
}
posted @ 2022-06-27 20:06  Administrator-09  阅读(3)  评论(0编辑  收藏  举报