ACM NFLSOJ #834 - 【2021六校联考WC #2】三角形(找性质+数位 dp)

发布时间 2023-03-31 18:32:42作者: tzc_wk

首先先手玩一下所有点的 \(x\) 都相同的情况,你会发现存在解的必要条件是所有黑点的 \(y\) 构成一段连续的区间,此时答案为 \((X+R-L,L)\),其中 \(L,R\) 为所有点中纵坐标的最小和最大值。

受这个思想启发,我们考虑将所有点都变到同一 \(x\) 坐标上,设 \(X=\min\{x_i\}\)。那么显然将所有点的横坐标都变得相同的方法是,每次操作最右边的点,直到所有点横坐标都为 \(X\)。注意到这个过程有点像折线,每次向左或左上走一步,因此容易计算出最终一个点 \((X,t)\) 的权值情况:\(v_{X,t}=(\sum\limits_{i=1}^n\dbinom{x_i-X}{t-y_i})\bmod 2\)。又因为题目保证有解,所以最终权值为 \(1\) 的点会组成一段连续的区间,因此我们只需要求出任意一个权值为 \(1\) 的点,就可以向上和向下分别倍增得到上下边界。

接下来考虑怎么找到任意一个权值为 \(1\) 的点。有个神奇的结论是,考察所有纵坐标模 \(3\)\(k\) 的点,计算这些点的权值的异或和,那么必然存在一个 \(k\in\{0,1,2\}\),使得该异或和为 \(1\)。那么我们就枚举这个 \(k\) 好了,由于异或和为 \(1\),所以考虑二分,二分得到的左子区间和又子区间中必然恰有一个异或和还是 \(1\),直接往对应的子区间里走即可。因此问题相当于计算一个区间 \([l,r]\)\(Y\)\(3\)\(k\) 的所有 \((X,Y)\) 权值的异或和。由于组合数 \(\bmod 2\) 可以用 bitmask 的包含表示,因此这部分的求解可以数位 DP。然后就做完了。

const int MAXN=1e4;
const ll INF=1e18;
int n;ll x[MAXN+5],y[MAXN+5],mnx=INF;
bool test(ll X,ll Y){
	bool res=0;
	for(int i=1;i<=n;i++)if(X<=x[i]&&Y>=y[i])
		res^=((x[i]-X)&(Y-y[i]))==(Y-y[i]);
	return res;
}
bool calc(ll lim,ll a,ll rem){
	static int dp[64][3][2];memset(dp,0,sizeof(dp));dp[62][0][1]=1;
	for(int i=61;~i;i--)for(int j=0;j<3;j++)for(int k=0;k<2;k++)if(dp[i+1][j][k]){
		int up=min(a>>i&1,(k)?(lim>>i&1):1);
		for(int l=0;l<=up;l++)dp[i][(j+l*(1ll<<i))%3][k&(l==(lim>>i&1))]^=dp[i+1][j][k];
	}
//	eprintf("! %lld %lld %lld %d\n",lim,a,rem,dp[0][rem][0]^dp[0][rem][1]);
	return dp[0][rem][0]^dp[0][rem][1];
}
bool check(ll l,ll r,int rem){
	bool flg=0;
	for(int i=1;i<=n;i++){
		ll L=max(l,y[i]),R=min(r,y[i]+x[i]-mnx);
		if(L>R)continue;
		flg^=calc(R-y[i],x[i]-mnx,(rem-y[i]%3+3)%3);
		if(L-y[i]>0)flg^=calc(L-y[i]-1,x[i]-mnx,(rem-y[i]%3+3)%3);
	}return flg;
}
int main(){
//	freopen("in.txt","r",stdin);
	freopen("triangle.in","r",stdin);
	freopen("triangle.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%lld%lld",&x[i],&y[i]),chkmin(mnx,x[i]);
	for(int k=0;k<3;k++)if(check(-INF,INF,k)){
		ll l=-INF,r=INF;
		while(l<r){
			ll mid=l+r>>1;
			if(check(l,mid,k))r=mid;
			else l=mid+1;
		}
//		eprintf("! %lld %lld\n",mnx,l);
		assert(test(mnx,l));
		for(int i=60;~i;i--)if(test(mnx,l-(1ll<<i)))l-=(1ll<<i);
		for(int i=60;~i;i--)if(test(mnx,r+(1ll<<i)))r+=(1ll<<i);
		printf("%lld %lld\n",mnx+(r-l),l);
		return 0;
	}
	return 0;
}