【LOJ3179】「IOI2019」视觉程序

【题目链接】

  • 点击打开链接

【思路要点】

  • 考虑 K = 1 K=1 K=1 的情况,我们需要判断是否存在相邻的黑色像素。
  • 如果我们知道两个黑色像素的相对方向,则可以采取如下策略:
  • 不妨令两个黑色像素在同一列,计算每一行的 o r or or ,并计算得到的数组的前缀 o r or or 与后缀 o r or or ,记为 p r e i , s u f i pre_i,suf_i prei,sufi 。若存在一个 i i i 使得 p r e i & s u f i + 2 pre_i\&suf_{i+2} prei&sufi+2 1 1 1 ,则说明两个黑色像素所在的行相距至少为 2 2 2 ,从而可以判断它们是否相邻。
  • 在不知道两个黑色像素的相对方向时,可以采取如下策略:
  • 若将坐标系进行旋转 ( x , y ) → ( x − y , x + y ) (x,y)\rightarrow(x-y,x+y) (x,y)(xy,x+y) ,则可以将两个点 ( x , y ) , ( a , b ) (x,y),(a,b) (x,y),(a,b) 的曼哈顿距离 ∣ x − a ∣ + ∣ y − b ∣ |x-a|+|y-b| xa+yb 转化为切比雪夫距离 max ⁡ { ∣ x − a ∣ , ∣ y − b ∣ } \max\{|x-a|,|y-b|\} max{ xa,yb} ,从而在两维坐标上都进行上文的判断即可。
  • 注意到上面的过程本质上判断了两个黑色像素的距离是否 ≥ K + 1 \geq K+1 K+1 ,那么再判断一下两个黑色像素的距离是否 ≥ K \geq K K 就行了。
  • N , M N,M N,M 同阶,时间复杂度 O ( N 2 ) O(N^2) O(N2) ,使用操作次数 O ( N ) O(N) O(N) ,涉及元素个数 O ( N 2 ) O(N^2) O(N2)
  • 以下代码没有考虑一些可能的对操作次数的常数优化。

【代码】

#include "vision.h"
#include
using namespace std;
const int MAXN = 405;
int num[MAXN][MAXN], tot;
int pre[MAXN], suf[MAXN];
int getbool(int n, int m, int k) {
      
	for (int i = 2; i <= n + m; i++) {
      
		vector <int> opt; opt.clear();
		for (int j = 1; j <= n; j++) {
      
			int k = i - j;
			if (k >= 1 && k <= m) opt.push_back(num[j][k]);
		}
		if (i != 2) opt.push_back(pre[i - 1]);
		pre[i] = add_or(opt);
	}
	for (int i = n + m; i >= 2; i--) {
      
		vector <int> opt; opt.clear();
		for (int j = 1; j <= n; j++) {
      
			int k = i - j;
			if (k >= 1 && k <= m) opt.push_back(num[j][k]);
		}
		if (i != n + m) opt.push_back(suf[i + 1]);
		suf[i] = add_or(opt);
	}
	vector <int> opt; opt.clear();
	for (int i = 2, j = i + k; j <= n + m; i++, j++) {
      
		vector <int> tmp; tmp.clear();
		tmp.push_back(pre[i]), tmp.push_back(suf[j]);
		opt.push_back(add_and(tmp));
	}
	int resa = add_or(opt);
	for (int i = 1; i <= n; i++)
	for (int j = 1, k = m; j <= k; j++, k--)
		swap(num[i][j], num[i][k]);
	
	for (int i = 2; i <= n + m; i++) {
      
		vector <int> opt; opt.clear();
		for (int j = 1; j <= n; j++) {
      
			int k = i - j;
			if (k >= 1 && k <= m) opt.push_back(num[j][k]);
		}
		if (i != 2) opt.push_back(pre[i - 1]);
		pre[i] = add_or(opt);
	}
	for (int i = n + m; i >= 2; i--) {
      
		vector <int> opt; opt.clear();
		for (int j = 1; j <= n; j++) {
      
			int k = i - j;
			if (k >= 1 && k <= m) opt.push_back(num[j][k]);
		}
		if (i != n + m) opt.push_back(suf[i + 1]);
		suf[i] = add_or(opt);
	}
	opt.clear();
	for (int i = 2, j = i + k; j <= n + m; i++, j++) {
      
		vector <int> tmp; tmp.clear();
		tmp.push_back(pre[i]), tmp.push_back(suf[j]);
		opt.push_back(add_and(tmp));
	}
	int resb = add_or(opt);
	for (int i = 1; i <= n; i++)
	for (int j = 1, k = m; j <= k; j++, k--)
		swap(num[i][j], num[i][k]);
	opt.clear();
	opt.push_back(resa), opt.push_back(resb);
	return add_or(opt);
}
void construct_network(int n, int m, int k) {
      
	for (int i = 1; i <= n; i++)
	for (int j = 1; j <= m; j++)
		num[i][j] = tot++;
	if (k == n + m - 2) {
      
		getbool(n, m, k);
		return;
	}
	int greater = getbool(n, m, k + 1);
	int geq = getbool(n, m, k);
	int ngr = add_not(greater);
	vector <int> opt; opt.clear();
	opt.push_back(geq), opt.push_back(ngr);
	add_and(opt);
}

你可能感兴趣的:(【OJ】LOJ,【类型】做题记录,【资料】交互题,【算法】旋转坐标系,【算法】差分与前缀和思想)