AtCoder Beginner Contest 164 F I hate Matrix Construction 状压+固定行修改列

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 <stdio.h>
#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]<<k);//第k位的处理
	}
	for(i=1;i<=n;i++){//行检验
		ull c=ans[i][1];
		for(j=2;j<=n;j++)
			if(s[i]==0)c=c&ans[i][j];
			else c=c|ans[i][j];
		if(c!=u[i])return 0*printf("-1\n");
	}
	for(j=1;j<=n;j++){//列检验
		ull c=ans[1][j];
		for(i=2;i<=n;i++)
			if(t[j]==0)c=c&ans[i][j];
			else c=c|ans[i][j];
		if(c!=v[j])return 0*printf("-1\n");
	}
	for(i=1;i<=n;i++){//经历了考验
		for(j=1;j<=n;j++)
			printf("%llu ",ans[i][j]);
		printf("\n");
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值