P6751 [IOI2019] 视觉程序 题解
我的做法是二进制加法器。似乎大多数人都不是这个做法?
首先,查询每一行、每一列的异或和。
以每一行为例,如果两个黑色像素在同一行,那么所有行的异或和都为
如果我们把这个异或和再做一遍前缀异或和,那么黑色像素的竖直距离就是前缀异或和中
每一列同理。注意到行的前缀异或和中,最后一个数一定为
那么如何统计前缀异或和中
我们想到了二进制加法器:从
我们要统计的数(距离)最多为
统计出
我的代码使用的指令数最多为
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
int add_not(int);
int add_and(vector<int> Ns);
int add_or(vector<int> Ns);
int add_xor(vector<int> Ns);
int n,m,k,cnt,zero,one;
vector<int> vec;
inline void add(int pos){
int bg=cnt-16;
add_xor({bg,pos});
cnt=add_and({bg,pos});
for(int i=1;i<8;++i){
add_xor({bg+i*2,cnt});
cnt=add_and({bg+i*2,cnt});
}
cnt=add_xor({bg+16,cnt});
}
void construct_network(int H,int W,int K){
n=H;m=W;k=K;
vec.resize(m);
for(int i=0;i<m;++i)vec[i]=i;
cnt=add_xor(vec);
vec.resize(m+1);
for(int i=1;i<n;++i){
for(int j=0;j<m;++j)vec[j]=i*m+j;
vec[m]=cnt;
cnt=add_xor(vec);
}
vec.resize(n+1);
for(int i=0;i<m;++i){
for(int j=0;j<n;++j)vec[j]=j*m+i;
vec[n]=cnt;
cnt=add_xor(vec);
}
zero=add_xor({0,0});
for(int i=1;i<17;++i)
cnt=add_or({zero});
for(int i=n*m;i<n*m+n+m;++i)add(i);
int bg=cnt-16;
vec.resize(9);
one=add_not(zero);
for(int i=0;i<9;++i)
vec[i]=add_xor({bg+2*i,(k>>i)&1?one:zero});
add_not(add_or(vec));
}