【LOJ3179】「IOI2019」视觉程序

喜你入骨 提交于 2019-11-27 07:23:17

【题目链接】

【思路要点】

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

【代码】

#include "vision.h"
#include<bits/stdc++.h>
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);
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!