AtCoder Beginner Contest 164 比赛人数11302 比赛开始后15分钟看到所有题
AtCoder Beginner Contest 164 F I hate Matrix Construction 状压+固定行修改列
总目录详见https://blog.csdn.net/mrcrack/article/details/104454762
在线测评地址https://atcoder.jp/contests/abc164/tasks/abc164_f
核心思路:
1列数据经过 与运算,结果是0,那么这列数据的数值中至少包含一个0.
1列数据经过 与运算,结果是1,那么这列数据的数值全是1.
1列数据经过 或运算,结果是0,那么这列数据的数值全是0.
1列数据经过 或运算,结果是1,那么这列数据的数值中至少包含一个1.
样例模拟如下
2
1 1
1 0
15 15
15 11
由行数据,得到矩阵基本轮廓
15 15
15 15
二进制形式
1111 1111
1111 1111
一位一位,用列操作来检验
第1列 1 15(1111)第1列经过或运算,结果是15(1111)
矩阵二进制形式
1111 1111
1111 1111
第2列 0 11(1011)第2列经过与运算,结果是11(1011)
矩阵二进制形式
1111 1011
1111 1111
矩阵十进制形式
15 11
15 15
具体思路可见代码,相信读者如有状压基础,还是容易看懂的。
AC代码如下
#include
#define ull unsigned long long
#define maxn 505
int s[maxn],t[maxn],br[maxn],bc[maxn],cnt[maxn],a[maxn][maxn];//s[0]:row,and;s[1]:row:or.t[0]:col,and;t[1]:col,or;
ull u[maxn],v[maxn],ans[maxn][maxn];
int main(){
int i,j,n,k;
scanf("%d",&n);
for(i=1;i<=n;i++)scanf("%d",&s[i]);
for(i=1;i<=n;i++)scanf("%d",&t[i]);
for(i=1;i<=n;i++)scanf("%llu",&u[i]);
for(i=1;i<=n;i++)scanf("%llu",&v[i]);
for(k=0;k<64;k++){//逐位判定,状压
for(i=1;i<=n;i++)br[i]=((u[i]>>k)&1),bc[i]=((v[i]>>k)&1);//获得第k位上的值
int hr[4]={0,0,0,0},hc[4]={0,0,0,0};//hr{(row,and,0),(row,and,1),(row,or,0),(row,or,1)}统计数量
for(i=1;i<=n;i++)//行列计算
hr[s[i]*2+br[i]]++,hc[2*t[i]+bc[i]]++;//hc{(col,and,0),(col,and,1),(col,or,0),(col,or,1)}
if(hr[2]&&hc[1])return 0*printf("-1\n");//粗筛(row,or,0),(col,and,1),两者不能同时有取值
if(hc[2]&&hr[1])return 0*printf("-1\n");//粗筛(col,or,0),(row,and,1),两者不能同时有取值
for(i=1;i<=n;i++){//用行操作后的值,来设定初值
cnt[i]=n;//i行上,雷同的数据有n个
for(j=1;j<=n;j++)
a[i][j]=br[i];//可以这样设置的理由:1&1=1,0&0=0;1|1=1,0|0=0;
}
for(j=1;j<=n;j++)//修改数据,符合列要求
if(t[j]!=bc[j])//t[j]表示第j列操作数(0,and;1,or),bc[j]记录第j列操作结果的第k位置值.全1,全0情况
for(i=1;i<=n;i++)
if(a[i][j]!=bc[j])cnt[i]--,a[i][j]=bc[j];//列上操作,同一行雷同元素需减少。
for(j=1;j<=n;j++){//用列操作后的值,来进行修补
int flag=0;//判定是否找到
if(t[j]==bc[j]){//(col,and,0,0)或者(col,or,1,1)
for(i=1;i<=n;i++)
if(a[i][j]==bc[j]){flag=1;break;}//找到这个为0,或为1的位置,找到一个即可。
if(flag)continue;//若找到,继续找下一行
for(i=1;i<=n;i++)//继续找
if(s[i]==br[i]&&cnt[i]>=2){//s[i]表示第i行操作数(0,and;1,or),br[i]记录第i行操作结果的第k位置值,(0,and,0)至少有一个0;(1,or,1)至少有一个1.cnt[i]>=2至少有两个雷同
cnt[i]--,a[i][j]=bc[j];//因至少有两个雷同,故可更改
break;
}
}
//存在兜了一圈,没改成功的情况,不用担心,之后,会有检查。
}
for(i=1;i<=n;i++)//生成待定矩阵元素
for(j=1;j<=n;j++)
ans[i][j]|=((ull)a[i][j]<