team2012-E1-mysol-0003
从 Trac 迁移的文章
这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。
原文章内容如下:
{{{
/* 解题报告 //
给出 k 个 n 位的编码,对于每次询问,判断是否能组合成所询问的编码
组合意味着选取一些编码(可以重复)进行异或操作,并可产生至多三个错误位
首先,题目要求是『用 k 个码组合成一个码』,这一点可以令人想到线性组合
确切地说,这题本质是在一个模 2 的线性空间上的,判定线性方程是否有解的题
如果能想到上面那点,做法就容易想到了:对 k 个码进行高斯消元
就只会剩下至多 n 个码,每位最多有一个代表元
对于输入的码,枚举错误的位数和错误的位置,对于枚举的每种情况,进行判定
判定方法则是扫一下每一位,如果这一位是 1,则用相应的代表元去异或
扫完后,如果剩下的值非零,那么对于枚举的这种情况,线性方程是无解的
另外,在枚举时注意一下顺序关系(先0后1),就可以找到字典序最小的解了
// By 猛犸也钻地 @ 2012.07.18 */
}}}
{{{
#include <cstdio>
#include <algorithm>
using namespace std;
int getcode(){
char s[35];
scanf("%s",s);
int ret=0;
for(int i=0;s[i];i++) ret|=(s[i]-'0')<<i;
return ret;
}
int a[20000],u[30],ans;
int guass(int n, int m){
int ret=0;
for(int x=0;x<m;x++){
int i=ret;
while(i<n && ~a[i]&1<<x) i++;
if(i==n) continue;
u[x]=a[i];
swap(a[i],a[ret++]);
for(i=ret;i<n;i++) if(a[i]&1<<x) a[i]^=u[x];
}
return ret;
}
bool check(int x, int m, int c, int i = 0){
if(!c){
ans=x;
for(int i=0;i<m;i++) if(x&1<<i) x^=u[i];
return !x;
}
if(i>=m) return false;
if(x&1<<i){
if(check(x^(1<<i),m,c-1,i+1)) return true;
if(check(x,m,c,i+1)) return true;
}else{
if(check(x,m,c,i+1)) return true;
if(check(x^(1<<i),m,c-1,i+1)) return true;
}
return false;
}
int main(){
int n,m,ask;
while(scanf("%d%d%d",&m,&n,&ask)==3){
for(int i=0;i<m;i++) u[i]=0;
for(int i=0;i<n;i++) a[i]=getcode();
n=guass(n,m);
while(ask--){
int x=getcode(),c;
for(c=0;c<=3;c++) if(check(x,m,c)) break;
if(c<4){
for(int i=0;i<m;i++) putchar(ans&1<<i?'1':'0');
puts("");
}else{
puts("NA");
}
}
}
}
}}}
/* 解题报告 //
给出 k 个 n 位的编码,对于每次询问,判断是否能组合成所询问的编码
组合意味着选取一些编码(可以重复)进行异或操作,并可产生至多三个错误位
首先,题目要求是『用 k 个码组合成一个码』,这一点可以令人想到线性组合
确切地说,这题本质是在一个模 2 的线性空间上的,判定线性方程是否有解的题
如果能想到上面那点,做法就容易想到了:对 k 个码进行高斯消元
就只会剩下至多 n 个码,每位最多有一个代表元
对于输入的码,枚举错误的位数和错误的位置,对于枚举的每种情况,进行判定
判定方法则是扫一下每一位,如果这一位是 1,则用相应的代表元去异或
扫完后,如果剩下的值非零,那么对于枚举的这种情况,线性方程是无解的
另外,在枚举时注意一下顺序关系(先0后1),就可以找到字典序最小的解了
// By 猛犸也钻地 @ 2012.07.18 */
#include <cstdio>
#include <algorithm>
using namespace std;
int getcode(){
char s[35];
scanf("%s",s);
int ret=0;
for(int i=0;s[i];i++) ret|=(s[i]-'0')<<i;
return ret;
}
int a[20000],u[30],ans;
int guass(int n, int m){
int ret=0;
for(int x=0;x<m;x++){
int i=ret;
while(i<n && ~a[i]&1<<x) i++;
if(i==n) continue;
u[x]=a[i];
swap(a[i],a[ret++]);
for(i=ret;i<n;i++) if(a[i]&1<<x) a[i]^=u[x];
}
return ret;
}
bool check(int x, int m, int c, int i = 0){
if(!c){
ans=x;
for(int i=0;i<m;i++) if(x&1<<i) x^=u[i];
return !x;
}
if(i>=m) return false;
if(x&1<<i){
if(check(x^(1<<i),m,c-1,i+1)) return true;
if(check(x,m,c,i+1)) return true;
}else{
if(check(x,m,c,i+1)) return true;
if(check(x^(1<<i),m,c-1,i+1)) return true;
}
return false;
}
int main(){
int n,m,ask;
while(scanf("%d%d%d",&m,&n,&ask)==3){
for(int i=0;i<m;i++) u[i]=0;
for(int i=0;i<n;i++) a[i]=getcode();
n=guass(n,m);
while(ask--){
int x=getcode(),c;
for(c=0;c<=3;c++) if(check(x,m,c)) break;
if(c<4){
for(int i=0;i<m;i++) putchar(ans&1<<i?'1':'0');
puts("");
}else{
puts("NA");
}
}
}
}