CF963-Div2-E. Xor-Grid Problem
CF963-Div2-E. Xor-Grid Problem
题意
给定一个
- 选择一行,把每个数变成所在列所有数的异或之和。
- 选择一列,把每个数变成所在行所有数的异或之和。
求进行任意次操作后整个矩阵最小的美丽值。
思路
第一个发现:同一数异或两次相当于没有异或,基于这个性质,我们首先可以发现连续两次对同一行(列)操作相当于没有操作。
第二个发现:能替换的值其实是很有限的,因为它是由一行或一列所有值的异或一起决定的,我们会忍不住把所有可供替换的值规规整整地写在对应行(列)前面,以样例中的第
你仔细观察这个矩阵或者手动操作几次,可以发现对某行(列)操作相当于和第
所以题目就转化成了:给定一个
实现
交换次数是无限的,但矩阵的状态是有限的。
朴素想法是枚举所有的状态,全部求一遍美丽值。
肯定要枚举哪行哪列不选,还要枚举每行每列的排列方式,最后对整个矩阵求答案。所以直接做的话应该是
首先可以发现其实行和列的贡献是分别独立的。以行为例,如果行的排列方式确定了,那竖着的贡献就是固定的。这是由操作方式决定的,题目给出的两种操作能保证初始同行(列)的元素到最后仍然同行(列),变的只是行之间和列之间的顺序。复杂度变为
继续优化答案统计。每次都重新求一遍整个矩形的贡献实在是太不划算了。又想到对于一个选行(列)的集合
记
记
有转移
其中
最后答案就是
时间复杂度
注意 dp 三层循环的顺序,先枚举
#include<bits/stdc++.h>
#define F(i,l,r) for(int i(l);i<=(r);++i)
#define G(i,r,l) for(int i(r);i>=(l);--i)
using namespace std;
using ll = long long;
const int inf = 0x3f3f3f3f;
int f[66000][18][18], g[66000][18][18];
int s[18][18], ss[18][18], a[18][18];
int T, n, m;
void init(){
cin >> n >> m;
F(s, 0, (1 << (n + 1)) - 1) F(i, 0, n) F(j, 0, m) f[s][i][j] = inf;
F(s, 0, (1 << (m + 1)) - 1) F(i, 0, m) F(j, 0, n) g[s][i][j] = inf;
F(i, 0, n) F(j, 0, n) s[i][j] = 0;
F(i, 0, m) F(j, 0, m) ss[i][j] = 0;
F(i, 1, n) F(j, 1, m) cin >> a[i][j];
F(j, 1, m){
a[0][j] = 0;
F(i, 1, n) a[0][j] ^= a[i][j];
}
F(i, 0, n){
a[i][0] = 0;
F(j, 1, m) a[i][0] ^= a[i][j];
} // ok
F(i, 0, n) F(k, i + 1, n) {
s[i][k] = 0;
F(j, 0, m) s[i][k] += abs(a[i][j] - a[k][j]);
s[k][i] = s[i][k];
}
F(i, 0, m) F(k, i + 1, m) {
ss[i][k] = 0;
F(j, 0, n) ss[i][k] += abs(a[j][i] - a[j][k]);
ss[k][i] = ss[i][k];
}
}
int solve(){
F(i, 0, n) F(j, 0, m) f[1 << i][i][j] = 0; // 只考虑行
F(j, 0, m) F(i, 0, n) g[1 << j][j][i] = 0; // 只考虑列
F(j, 0, m){
F(S, 1, (1 << (n + 1)) - 1){
F(i, 0, n){
if(!((S >> i) & 1) || f[S][i][j] == inf) continue;
F(k, 0, n){
if(i == k || ((S >> k) & 1)) continue;
f[S + (1 << k)][k][j] = min(f[S + (1 << k)][k][j], f[S][i][j] + s[i][k] - abs(a[i][j] - a[k][j]));
}
}
}
}
F(j, 0, n){
F(S, 1, (1 << (m + 1)) - 1){
F(i, 0, m){
if(!((S >> i) & 1) || g[S][i][j] == inf) continue;
F(k, 0, m){
if(i == k || ((S >> k) & 1)) continue;
g[S + (1 << k)][k][j] = min(g[S + (1 << k)][k][j], g[S][i][j] + ss[i][k] - abs(a[j][i] - a[j][k]));
}
}
}
}
int ans = inf, U1 = (1 << (n + 1)) - 1, U2 = (1 << (m + 1)) - 1;
F(i, 0, n){
F(j, 0, m){
int ret1 = inf;
F(k, 0, n) if(k != i) {
ret1 = min(ret1, f[U1 - (1 << i)][k][j]);
}
int ret2 = inf;
F(k, 0, m) if(k != j) {
ret2 = min(ret2, g[U2 - (1 << j)][k][i]);
}
ans = min(ans, ret1 + ret2);
}
}
return ans;
}
signed main(){
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> T;
while(T --){
init();
cout << solve() << '\n';
}
return fflush(0), 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效