【NOI模拟赛】计算几何(凸包,暴力,并查集)

31 篇文章 1 订阅
这篇博客介绍了如何使用并查集解决一个计算几何问题,具体是求解平面上大量整点构成的凸包面积。题目中数据规模巨大,但坐标值域较小,可以通过寻找坐标循环来优化算法。博主提出利用横坐标对应纵坐标的最小值和最大值,通过鸽笼原理找到坐标循环,并用并查集维护已赋值的横坐标段,实现了O(p log p)的时间复杂度解决方案。
摘要由CSDN通过智能技术生成

题面

一句话题意:给定平面上 n n n 个整点,求构成的凸包面积大小乘 2 的值。

数据生成方式:

给定 x 0 , y 0 , a x , a y , b x , b y , p x , p y , n x_0,y_0,a_x,a_y,b_x,b_y,p_x,p_y,n x0,y0,ax,ay,bx,by,px,py,n
x i = ( a x x i − 1 + b x ) m o d    p x y i = ( a y y i − 1 + b y ) m o d    p y x_i=(a_xx_{i-1}+b_x)\mod p_x\\ y_i=(a_yy_{i-1}+b_y)\mod p_y xi=(axxi1+bx)modpxyi=(ayyi1+by)modpy

最后得到 n n n 个点 ( x 0 , y 0 ) , ( x 1 , y 1 ) , . . . , ( x n − 1 , y n − 1 ) (x_0,y_0),(x_1,y_1),...,(x_{n-1},y_{n-1}) (x0,y0),(x1,y1),...,(xn1,yn1)

n ≤ 1018

px,py ≤ 200000

题解

我们发现这个 n 特别大,但是坐标值域比较小。

相信每个做过平面凸包的人都有过这样一个梦想:将横坐标每一个值对应的最小纵坐标和最大纵坐标求出来,当成两个序列做斜率优化求凸包。但是无奈值域通常是非常大的,这个想法一般用不了。

这道题就不一样了,至于比较小,我们完全可以求每一个横坐标对应的最小纵坐标和最大纵坐标。

根据鸽笼原理,n 特别大的时候横坐标和纵坐标一定会形成循环。

我们求出每一个纵坐标第一次出现的时刻(可以这么说吧)和第二次出现的时刻(如果有的话),然后从小到大/从大到小枚举纵坐标给横坐标赋值。由于不难发现只会形成一个循环,所以每个位置的第二时刻和第一时刻的差都是相等的。

我们对于每个纵坐标枚举对应的横坐标,暴力赋值,这其中需要跳过已经赋值过的横坐标。由于不管纵坐标是什么,相同纵坐标的下一个横坐标是仅取决于当前横坐标的,所以我们可以用并查集维护已经赋值过的连续段,确保可以 O ( log ⁡ p ) O(\log p) O(logp) 跳过。

时间复杂度 O ( p log ⁡ p ) O(p\log p) O(plogp)

CODE

#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<random>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
#pragma GCC optimize(2)
using namespace std;
#define MAXN 200005
#define LL long long
#define ULL unsigned long long
#define ENDL putchar('\n')
#define DB double
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
#define PR pair<int,int>
#define UIN unsigned int
#define SQ 317
int xchar() {
	static const int maxn = 1000000;
	static char b[maxn];
	static int pos = 0,len = 0;
	if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);
	if(pos == len) return -1;
	return b[pos ++];
}
// #define getchar() xchar()
inline LL read() {
	LL f = 1,x = 0;int s = getchar();
	while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
	while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}
	return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
inline void putnum(LL x) {
	if(!x) {putchar('0');return ;}
	if(x<0) putchar('-'),x = -x;
	return putpos(x);
}
inline void AIput(LL x,int c) {putnum(x);putchar(c);}

int n,m,s,o,k;
int l[MAXN],r[MAXN];
int P = 1;
struct it{
	int a,b;LL c;
	it(){a=1;b=0;c=0;}
	it(int A,int B,LL C){a=A;b=B;c=C;}
}kx[MAXN],ky[MAXN];
inline it operator * (it l,it r) {
	l.a = l.a*1ll*r.a % P;
	l.b = (l.b*1ll*r.a + r.b) % P;
	l.c += r.c; return l;
}
inline int operator * (int x,it y) {
	return (x*1ll*y.a + y.b) % P;
}
PR fr[MAXN];
it la[MAXN];
int fa[MAXN];
it fe[MAXN];
int findf(int x) {
	if(x == fa[x]) return x;
	int r = fa[x],f = findf(r);
	fe[x] = fe[x]*fe[r]; fa[x] = f;
	return f;
}
void unionSet(int u,int f,it k) {
	fa[u] = findf(f); fe[u] = k*fe[f];
}
PR ar[MAXN<<1];
int ca;
int st[MAXN],tp;
LL S(PR a,PR b,PR c) {
	PR A = {a.FI-b.FI,a.SE-b.SE};
	PR B = {c.FI-b.FI,c.SE-b.SE};
	LL rs = A.SE*1ll*B.FI - A.FI*1ll*B.SE;
	return rs<0 ? -rs:rs;
}
int main() {
	freopen("geometry.in","r",stdin);
	freopen("geometry.out","w",stdout);
	int x0 = read(),y0 = read();
	int ax = read(),ay = read();
	int bx = read(),by = read();
	it tx = it(ax,bx,1),ty = it(ay,by,1);
	n = read();m = read();
	LL N = read();
	P = n;
	for(int i = 1;i < n;i ++) kx[i] = kx[i-1]*tx;
	P = m;
	for(int i = 1;i < m;i ++) ky[i] = ky[i-1]*ty;
	for(int i = 0;i < n;i ++) {
		l[i] = m+1; r[i] = -1;
		fa[i] = i; fe[i] = it();
	}
	P = m;
	int p = y0,ct = 0,xx = x0;
	while(!la[p].c && ct < N) {
		if(!fr[p].FI) {
			fr[p] = {ct+1,xx};
		}
		else if(!la[p].c) {
			la[p] = kx[ct-fr[p].FI+1];
		}
		p = p*ty; ct ++;
		P = n; xx = xx*tx; P = m;
	}
	P = n;
	for(int i = 0;i < m;i ++) {
		if(fr[i].FI) {
			int p = fr[i].SE;
			LL cn = fr[i].FI-1;
			findf(p); cn += fe[p].c;
			p = fa[p];
			while(cn < N) {
				l[p] = min(l[p],i);
				r[p] = max(r[p],i);
				if(!la[i].c) break;
				int nx = p*la[i];
				if(findf(nx) == p) break;
				unionSet(p,nx,la[i]);
				findf(p); cn += fe[p].c;
				p = fa[p];
			}
		}
	}
	for(int i = 0;i < n;i ++) fa[i] = i,fe[i] = it();
	for(int i = m-1;i >= 0;i --) {
		if(fr[i].FI) {
			int p = fr[i].SE;
			LL cn = fr[i].FI;
			findf(p); cn += fe[p].c;
			p = fa[p];
			while(cn <= N) {
				l[p] = min(l[p],i);
				r[p] = max(r[p],i);
				if(!la[i].c) break;
				int nx = p*la[i];
				if(findf(nx) == p) break;
				unionSet(p,nx,la[i]);
				findf(p); cn += fe[p].c;
				p = fa[p];
			}
		}
	}
	int ll = n-1,rr = 0;
	for(int i = 0;i < n;i ++) {
		if(l[i] < n && r[i] >= 0) {
			ll = min(ll,i); rr = max(rr,i);
		}
	}
	tp = 0;
	for(int i = ll;i <= rr;i ++) {
		if(l[i] >= n || r[i] < 0) continue;
		while(tp > 1 && (l[st[tp]]-l[st[tp-1]])*1ll*(i-st[tp]) >= (l[i]-l[st[tp]])*1ll*(st[tp]-st[tp-1])) {
			st[tp --] = 0;
		}
		st[++ tp] = i;
	}
	for(int i = 1;i <= tp;i ++) {
		ar[++ ca] = {st[i],l[st[i]]};
	}
	tp = 0;
	for(int i = ll;i <= rr;i ++) {
		if(l[i] >= n || r[i] < 0) continue;
		while(tp > 1 && (r[st[tp]]-r[st[tp-1]])*1ll*(i-st[tp]) <= (r[i]-r[st[tp]])*1ll*(st[tp]-st[tp-1])) {
			st[tp --] = 0;
		}
		st[++ tp] = i;
	}
	for(int i = tp;i > 0;i --) {
		ar[++ ca] = {st[i],r[st[i]]};
	}
	LL ans = 0;
	for(int i = 3;i <= ca;i ++) {
		ans += S(ar[1],ar[i-1],ar[i]);
	}
	AIput(ans,'\n');
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值