题解 一道计算几何的模板题

传送门

image

嗯。

  • 关于 Graham 算法求凸包的一些实现细节:
    选取最左上的点为基准点(定义为横坐标最小的点,如有相同选纵坐标最大的)
    注意若逆时针扫描,只有选最左上的点是正确的而选最左下不正确
    当按极角序排序时,若当前比较的两点横坐标相同纵坐标小的在前,若极角相等模长小的在前

    点我送凸包板子
    namespace convex{
    int top;
    double ans;
    const double eps=1e-9;
    pair<double, double> a[N], sta[N];
    struct vec{double x, y; vec(double a, double b):x(a),y(b){}};
    inline double operator ^ (vec a, vec b) {return a.x*b.y-b.x*a.y;}
    inline double dis(pair<double, double> a, pair<double, double> b) {
    	return sqrt((a.fir-b.fir)*(a.fir-b.fir)+(a.sec-b.sec)*(a.sec-b.sec));
    }
    inline bool isect(pair<double, double> a, pair<double, double> b, pair<double, double> c) {
    	return (vec(c.fir-a.fir, c.sec-a.sec)^vec(b.fir-a.fir, b.sec-a.sec))>=0;
    }
    inline double area(pair<double, double> a, pair<double, double> b, pair<double, double> c) {
    	double x=dis(a, b), y=dis(b, c), z=dis(a, c), p=(x+y+z)/2;
    	// cout<<"xyz: "<<fixed<<setprecision(5)<<x<<' '<<y<<' '<<z<<endl;
    	return round(sqrt(p*(p-x)*(p-y)*(p-z))*2);
    }
    double solve(int n) {
    	sort(a+1, a+n+1, [](pair<double, double> x, pair<double, double> y){
    		return fabs(x.fir-y.fir)<eps?x.sec<y.sec:x.fir<y.fir;
    	});
    	n=unique(a+1, a+n+1)-a-1;
    	sort(a+2, a+n+1, [](pair<double, double> x, pair<double, double> y){
    		if (x.fir==y.fir) return x.sec<y.sec;
    		double tem=vec(x.fir-a[1].fir, x.sec-a[1].sec)^vec(y.fir-a[1].fir, y.sec-a[1].sec);
    		if (fabs(tem)<eps) return dis(a[1], x)<dis(a[1], y);
    		else return tem>0;
    	});
    	// cerr<<"a: "; for (int i=1; i<=n; ++i) cerr<<"("<<a[i].fir<<','<<a[i].sec<<") "; cerr<<endl;
    	for (int i=1; i<=n; ++i) {
    		while (top>1 && isect(sta[top-1], sta[top], a[i])) --top;
    		sta[++top]=a[i];
    	}
    	// cout<<"sta: "; for (int i=1; i<=top; ++i) cout<<"("<<sta[i].fir<<','<<sta[i].sec<<") "; cout<<endl;
    	// for (int i=3; i<=top; ++i) ans+=area(sta[1], sta[i-1], sta[i]); //, cout<<"area: "<<area(sta[1], sta[i-1], sta[i])<<endl;
    	for (int i=2; i<=top; ++i) ans+=dis(sta[i-1], sta[i]);
    	ans+=dis(sta[1], sta[top]);
    	// sort(sta+1, sta+top+1);
    	// cerr<<"sta: "; for (int i=1; i<=top; ++i) cerr<<"("<<sta[i].fir<<','<<sta[i].sec<<") "; cerr<<endl;
    	return ans;
    }
    }
    
    本来还有几组 hack 数据的但是被我重启弄丢了

然后回到本题
发现 \(x\) 的循环节 \(k\)\(O(n)\) 级别的
发现能与 \(x\) 匹配的 \(y\) 是在 \(y\) 上以 \(k\) 为步长能跳到的所有点
发现只关心能跳到的最大,最小 \(y\)
发现在同一个跳的过程中构成的环上的点最大,最小相同
所以可以做到 \(O(n)\),需要处理大量边界情况
但因为数据是随的 \(O(n^2)\) 且完全不做任何处理也能过

点击查看代码
// ubsan: undefined
// accoders
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 400010
#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 ll read() {
	ll 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;
}

ll n;
ll x[N], y[N];
ll ax, ay, bx, by, px, py, kx;

namespace convex{
	ll ans;
	int top;
	pair<ll, ll> a[N], sta[N];
	struct vec{ll x, y; vec(ll a, ll b):x(a),y(b){}};
	inline ll operator ^ (vec a, vec b) {return a.x*b.y-b.x*a.y;}
	inline double dis(pair<ll, ll> a, pair<ll, ll> b) {
		return sqrt((a.fir-b.fir)*(a.fir-b.fir)+(a.sec-b.sec)*(a.sec-b.sec));
	}
	inline bool isect(pair<ll, ll> a, pair<ll, ll> b, pair<ll, ll> c) {
		return (vec(c.fir-a.fir, c.sec-a.sec)^vec(b.fir-a.fir, b.sec-a.sec))>=0;
	}
	inline double area(pair<ll, ll> a, pair<ll, ll> b, pair<ll, ll> c) {
		double x=dis(a, b), y=dis(b, c), z=dis(a, c), p=(x+y+z)/2;
		// cout<<"xyz: "<<fixed<<setprecision(5)<<x<<' '<<y<<' '<<z<<endl;
		return round(sqrt(p*(p-x)*(p-y)*(p-z))*2);
	}
	ll solve(int n) {
		sort(a+1, a+n+1, [](pair<ll, ll> x, pair<ll, ll> y){
			return x.fir==y.fir?x.sec<y.sec:x.fir<y.fir;
		});
		n=unique(a+1, a+n+1)-a-1;
		sort(a+2, a+n+1, [](pair<ll, ll> x, pair<ll, ll> y){
			if (x.fir==y.fir) return x.sec<y.sec;
			ll tem=vec(x.fir-a[1].fir, x.sec-a[1].sec)^vec(y.fir-a[1].fir, y.sec-a[1].sec);
			if (!tem) return dis(a[1], x)<dis(a[1], y);
			else return tem>0;
		});
		// cerr<<"a: "; for (int i=1; i<=n; ++i) cerr<<"("<<a[i].fir<<','<<a[i].sec<<") "; cerr<<endl;
		for (int i=1; i<=n; ++i) {
			while (top>1 && isect(sta[top-1], sta[top], a[i])) --top;
			sta[++top]=a[i];
		}
		// cout<<"sta: "; for (int i=1; i<=top; ++i) cout<<"("<<sta[i].fir<<','<<sta[i].sec<<") "; cout<<endl;
		for (int i=3; i<=top; ++i) ans+=area(sta[1], sta[i-1], sta[i]); //, cout<<"area: "<<area(sta[1], sta[i-1], sta[i])<<endl;
		// sort(sta+1, sta+top+1);
		// cerr<<"sta: "; for (int i=1; i<=top; ++i) cerr<<"("<<sta[i].fir<<','<<sta[i].sec<<") "; cerr<<endl;
		return ans;
	}
}

namespace force{
	void solve() {
		for (int i=1; i<n; ++i) {
			x[i]=(ax*x[i-1]+bx)%px;
			y[i]=(ay*y[i-1]+by)%py;
		}
		for (int i=0; i<n; ++i) convex::a[i+1]={x[i], y[i]};
		cerr<<"x: "; for (int i=0; i<n; ++i) cerr<<x[i]<<' '; cerr<<endl;
		cerr<<"y: "; for (int i=0; i<n; ++i) cerr<<y[i]<<' '; cerr<<endl;
		printf("%lld\n", convex::solve(n));
	}
}

namespace task1{
	int vis[N];
	vector<int> sta;
	int siz, loop, top;
	pair<int, int> range;
	int nxt[N], jmp[N], str[N];
	void solve() {
		siz=loop=n; range={0, n-1};
		memset(vis, -1, sizeof(vis));
		memset(jmp, -1, sizeof(jmp));
		memset(nxt, -1, sizeof(nxt));
		vis[x[0]]=0;
		for (int i=1; i<n; ++i) {
			x[i]=(ax*x[i-1]+bx)%px;
			if (~vis[x[i]]) {
				siz=i-vis[x[i]];
				range={vis[x[i]], i-1};
				for (int j=0; j<vis[x[i]]; ++j) {
					if (j) y[j]=(ay*y[j-1]+by)%py;
					convex::a[++top]={x[j], y[j]};
				}
				break;
			}
			else vis[x[i]]=i;
		}
		// cerr<<"siz: "<<siz<<endl;
		memset(vis, -1, sizeof(vis));
		vis[y[0]]=0;
		for (int i=1; i<n; ++i) {
			y[i]=(ay*y[i-1]+by)%py;
			nxt[y[i-1]]=y[i];
			if (~vis[y[i]]) {loop=i-vis[y[i]]; break;}
			else vis[y[i]]=i;
		}
		// cerr<<"loop: "<<loop<<endl;
		// cerr<<"top: "<<top<<endl;
		ll now=y[0]; str[0]=now;
		for (int i=1; i<=siz; ++i) str[i]=now=(ay*now+by)%py;
		for (int i=siz+1; i<=range.sec; ++i) str[i]=(ay*str[i-1]+by)%py;
		// cerr<<"str: "; for (int i=0; i<py; ++i) cerr<<str[i]<<' '; cerr<<endl;
		// cerr<<"nxt: "; for (int i=0; i<py; ++i) cerr<<nxt[i]<<' '; cerr<<endl;
		jmp[y[0]]=now;
		memset(vis, 0, sizeof(vis));
		sta.clear(); vis[y[0]]=1; sta.pb(y[0]);
		for (int pos=nxt[y[0]]; !vis[pos]&&pos!=-1; pos=nxt[pos])
			jmp[pos]=now=(ay*now+by)%py, vis[pos]=1, sta.pb(pos);
		while (sta.size()) vis[sta.back()]=0, sta.pop_back();
		// cerr<<"id : "; for (int i=0; i<py; ++i) cerr<<i<<' '; cerr<<endl;
		// cerr<<"jmp: "; for (int i=0; i<py; ++i) cerr<<jmp[i]<<' '; cerr<<endl;
		// cerr<<"range: "<<range.fir<<' '<<range.sec<<endl;
		for (int i=range.fir; i<=range.sec; ++i) {
			// cerr<<"at "<<i<<endl;
			ll maxy=str[i], miny=str[i];
			sta.clear(); vis[str[i]]=1; sta.pb(str[i]);
			for (ll y=jmp[str[i]],tim=i+siz; !vis[y]&&tim<n; y=jmp[y],tim+=siz)
				maxy=max(maxy, y), miny=min(miny, y), vis[y]=1, sta.pb(y); //, cerr<<"y="<<y<<endl;
			while (sta.size()) vis[sta.back()]=0, sta.pop_back();
			// cerr<<"add: ("<<x[i]<<','<<miny<<")"<<endl;
			// cerr<<"add: ("<<x[i]<<','<<maxy<<")"<<endl;
			convex::a[++top]={x[i], miny};
			convex::a[++top]={x[i], maxy};
		}
		printf("%lld\n", convex::solve(top));
	}
}

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

	x[0]=read(); y[0]=read(); ax=read(); ay=read(); bx=read(); by=read(); px=read(); py=read(); n=read();
	// force::solve();
	task1::solve();
	// n=read();
	// for (int i=1; i<=n; ++i) {
	// 	int x=read(), y=read();
	// 	convex::a[i]={x, y};
	// }
	// convex::solve(n);

	return 0;
}
posted @ 2022-07-30 07:57  Administrator-09  阅读(6)  评论(0编辑  收藏  举报