20220728

一道计算几何的模板题

点很多值域很小,考虑对每个横坐标求出最大/最小的纵坐标,只有这些点可能在凸包上

递推 \(O(p)\) 次后会进入循环节,前面的暴力。之后对于固定的 \(x_i\) 而言,只有前 \(py\) 个对应的 \(y\) 有用。把对应的 \(y\) 集合相同的 \(x_i\) 一起计算,相当于求 RMQ(这部分还是看码比较直观)
时间复杂度线性对数

赛时代码
const int inf = 0x3f3f3f3f;

struct Vec {
	LL x,y; Vec(LL x=0,LL y=0):x(x),y(y){}
	bool operator < (const Vec &a) const { return x!=a.x ? x<a.x : y<a.y; }
	friend bool operator == (const Vec &a,const Vec &b) { return !(a<b||b<a); }
	Vec operator - (const Vec &a) const { return {x-a.x,y-a.y}; }
	LL operator & (const Vec &a) const { return x*a.y-y*a.x; }
};

const int N = 4e5+5;
int n,ax,ay,bx,by,px,py,qx,qy,x[N],y[N],mx[N],mn[N];
LL m;
bool vis[N];
Vec p[N];
int stk[N],tp;

struct {
	int tn,t[N*4];
	void bld(const Vi &p) {
		tn = 1 << __lg(sz(p)+1)+1;
		memset(t,0,sizeof(int)*tn*2);
		Rep(i,0,sz(p)) t[tn+i+1] = y[p[i]];
		rFor(i,tn,1) t[i] = max(t[i<<1],t[i<<1|1]);
	}
	int qry(int l,int r) {
		int res = 0;
		for(l += tn, r += tn+2; l^r^1; l >>= 1, r >>= 1) {
			if( ~l & 1 ) ckmax(res,t[l^1]);
			if( r & 1 ) ckmax(res,t[r^1]);
		}
		return res;
	}
} t1;

struct {
	int tn,t[N*4];
	void bld(const Vi &p) {
		tn = 1 << __lg(sz(p)+1)+1;
		memset(t,0x3f,sizeof(int)*tn*2);
		Rep(i,0,sz(p)) t[tn+i+1] = y[p[i]];
		rFor(i,tn,1) t[i] = min(t[i<<1],t[i<<1|1]);
	}
	int qry(int l,int r) {
		int res = inf;
		for(l += tn, r += tn+2; l^r^1; l >>= 1, r >>= 1) {
			if( ~l & 1 ) ckmin(res,t[l^1]);
			if( r & 1 ) ckmin(res,t[r^1]);
		}
		return res;
	}
} t2;

signed main() {
#ifdef FS
	freopen("geometry2.in","r",stdin); freopen("geometry.out","w",stdout);
#else
	freopen("geometry.in","r",stdin); freopen("geometry.out","w",stdout);
#endif
	memset(mn,0x3f,sizeof mn);
	ios::sync_with_stdio(0);cout.tie(0);
	io>>x[1]>>y[1]>>ax>>ay>>bx>>by>>px>>py>>m;
	int mm = min((LL)2e5,m);
	For(i,2,mm) x[i] = ((LL)ax*x[i-1]+bx) %px, y[i] = ((LL)ay*y[i-1]+by) %py;
	For(i,1,mm) ckmax(mx[x[i]],y[i]), ckmin(mn[x[i]],y[i]);
	m -= mm, x[0] = x[mm], y[0] = y[mm];
	for(int i = 1; ; ++i) {
		x[i] = ((LL)ax*x[i-1]+bx) %px;
		if( x[i] == x[0] ) { qx = i; break; }
	}
	for(int i = 1; ; ++i) {
		y[i] = ((LL)ay*y[i-1]+by) %py;
		if( y[i] == y[0] ) { qy = i; break; }
	}
	Rep(i,0,qy) if( !vis[i] ) {
		Vi p;
		int j = i; do p.pb(j), vis[j] = 1, j = (j+qx)%qy; while( j != i );
		p.insert(p.end(),all(p)), t1.bld(p), t2.bld(p);
		Rep(l,0,sz(p)/2) for(j = p[l]; j < qx; j += qy) {
			if( j > m ) break;
			int r = min(sz(p)-1ll,l+(m-j)/qx);
			ckmax(mx[x[j]],t1.qry(l,r)), ckmin(mn[x[j]],t2.qry(l,r));
		}
	}
	Rep(i,0,px) if( mn[i] < inf ) p[++n] = {i,mx[i]}, p[++n] = {i,mn[i]};
	sort(p+1,p+n+1), n = unique(p+1,p+n+1)-p-1;
	auto push=[&](int i) {
		while( tp > 1 && (p[i]-p[stk[tp-1]] & p[stk[tp]]-p[stk[tp-1]]) > 0 )
			vis[stk[tp--]] = 0;
		vis[i] = 1, stk[++tp] = i;
	};
	memset(vis,0,sizeof vis);
	stk[++tp] = 1; For(i,2,n) push(i); rFor(i,n,1) if( !vis[i] ) push(i);
	LL ans=0; Rep(i,1,tp) ans += p[stk[i]] & p[stk[i+1]];
	cout<<ans;
	return 0;
}

菜肴挑选

不知道如何想到,所以只能生硬的给做法了

  • \(n\) 是奇数:把 \(n\) 个点等距离的放到圆上,枚举所有等腰三角形。这样任意一对点会被作为底边枚举 \(1\) 次,腰枚举 \(2\)
  • \(n\) 是偶数:拿前 \(n-1\) 个点做上述过程。猜想最终每条边算了\(6\) 次。可以对前 \(n-1\) 个点中距离为 \(1\) 的点 \(x,y\)\(2\)\((x,y,n)\),距离为 \(2\) 的点 \(x,y\)\(1\)\((x,y,n)\)。此时 \(n\) 与其他点都算了 \(6\) 次,前 \(n-1\) 个点中距离为 \(1\) 的算了 \(5\) 次,距离为 \(2\) 的算了 \(4\) 次,其余算了 \(3\) 次。在枚举所有腰长 \(>1\) 的等腰三角形即可
code
const int N = 1e3+5;
int n,m;
vector<tuple<int,int,int>> ans;

signed main() { freopen("dish.in","r",stdin); freopen("dish.out","w",stdout);
	io>>n, m = n-(~n&1);
	For(i,1,m) For(j,i+1,m)
		if( j-i & 1 ) {
			int k = j + (m-(j-i))/2;
			if( k > m ) k -= m;
			ans.pb(i,j,k);
		} else ans.pb(i,j,i+j>>1);
	if( ~n & 1 ) {
		For(i,1,m) ans.pb(i,i%m+1,n), ans.pb(i,i%m+1,n);
		For(i,1,m) ans.pb(i,(i+1)%m+1,n);
		For(i,1,m) For(j,2,m/2) {
			int x = i-j, y = i+j;
			if( x < 1 ) x += m;
			if( y > m ) y -= m;
			ans.pb(i,x,y);
		}
	}
	io<<sz(ans)<<endl;
	for(auto &i : ans) io<<get<0>(i)<<' '<<get<1>(i)<<' '<<get<2>(i)<<endl;
	return 0;
}

异或矩阵

crashed

对于这种构造方式较简单但数据规模很大的东西可以找一些倍增/递归的性质
打表找规律固然直观,但也不能忽视了宏观的把控

code
LL L,R,mx;
mint ans;

#define mid (l+r>>1)
void dfs(LL l,LL r,bool wh) {
	if( L <= l && r <= R ) {
		auto upd=[](LL x,mint y) {
			if( x > mx ) mx = x, ans = 0;
			if( x == mx ) ans += y;
		};
		int k = __lg(r-l+1); // r-l+1 = pow(2,k)
		upd(r-l,Pow(2,2*k));
		if( !wh ) {
			if( r < R ) upd((r-l)*2+1,2*Pow(2,k)*(R-r));
		} else {
			if( L < l ) upd((r-l)*2+1,2*Pow(2,k)*(l-L));
		}
		return;
	}
	if( L <= mid ) dfs(l,mid,0);
	if( mid < R ) dfs(mid+1,r,1);
}

void MAIN() {
	io>>L>>R;
	dfs(0,(1ll<<60)-1,0);
	io<<mx<<' '<<ans.x<<endl;
} signed main() { freopen("matrix.in","r",stdin); freopen("matrix.out","w",stdout);
	int T=read(); while( T-- ) MAIN(), mx = ans.x = 0;
	return 0;
}

官方题解

写个暴力发现答案是 \(2^{k}-1\)。若答案至少是 \(2^k\),那么存在两个格子分别为 \(2^{k}-1,2^{k}\),分类讨论:

  1. \((x,y),(x+1,y)\):由 \(x\oplus y=2^{k}-1,x+1\oplus y=2^{k}\) 可得 \(x\oplus x+1=2^{k+1}-1\),因此 \(x=A2^{k+1}+2^{k}-1,y=A2^{k+1}\)。存在这样的 \(x,y\) 的限制条件为 \(L\le y<x<R\)\(L\le A2^{k+1}\le R-2^{k}\Rightarrow L\le R-2^{k}-((R-2^{k})\bmod2^{k+1})\)
  2. \((x,y),(x-1,y)\):同理可得 \(x=A2^{k+1}+2^{k},y=A2^{k+1}+2^{k+1}-1\)\(L+2^{k}\le R-((R+1)\bmod2^{k+1})\)

找到满足条件的最大 \(k\),答案就是 \(2^{k+1}-1\)

计数部分没看懂

posted @ 2022-08-07 21:15  401rk8  阅读(34)  评论(0编辑  收藏  举报