繁凡出品的全新系列:解题报告系列 —— 超高质量算法题单,配套我写的超高质量题解和代码,题目难度不一定按照题号排序,我会在每道题后面加上题目难度指数(1∼51 \sim 51∼5),以模板题难度 111 为基准。


这样大家在学习算法的时候就可以执行这样的流程:

%
阅读我的【学习笔记】 / 【算法全家桶】学习算法 ⇒\Rightarrow⇒ 阅读我的相应算法的【解题报告】获得高质量题单 ⇒\Rightarrow⇒ 根据我的一句话题解的提示尝试自己解决问题 ⇒\Rightarrow⇒ 点开我的详细题解链接学习巩固(好耶)
%

题单链接:【解题报告】快速沃尔什变换FWT(ICPC / CCPC / NOIP / NOI / CF / AT / NC / P / BZOJ)超高质量题解 !!!

D、(CROC 2016 - Final Round C)Binary Table(矩阵 + 状态压缩 + FWT)(3.5)

Link

https://codeforces.com/contest/662/problem/C

Problem

给你一个 n×mn\times mn×m 的 010101 矩阵,每次操作:你可以挑选任意的某一行或者某一列翻转( 000 变 111,111 变 000 ),然后你需要使得整个矩阵的 111 的数量尽可能少,问你最少数量是多少。

n≤20,m≤105n≤20,m≤10^5n≤20,m≤105

Hint

一句话题解: 遇事不决先暴力,因为我们只有每一行或者每一列翻转或者不翻转,所以我们只需要考虑这个矩阵的初始状态以及每一行和每一列的翻转状态就行了。对于状态而言很容易想到状态压缩。我们可以很轻松地得到一个 O(2n×m)O(2^n\times m)O(2n×m) 的暴力算法,我们只需要状态压缩行翻转状态就行了。但是很明显过不去。那么我们能压缩行,为什么不能压缩列呢?对于行来说,行翻转状态只有 nnn 行 2n2^n2n 种情况,那么对于每一列来说,同样只有 nnn 行 2n2^n2n 种情况,所以我们就可以继续状压,然后发现从原状态到翻转后的状态不就是原状态与翻转状态的异或和吗?我们利用一些贪心的技巧,化简一下答案的式子,再使用经典FWT转换,就可以将问题转化为标砖的 FWT 的问题在 O(nlogn)O(nlogn)O(nlogn) 的时间复杂度下解决了 ~

Solution

我们发现序列翻转实际上就是全部的数与1做异或操作。我们将矩阵按列压成二进制。

先看数据:n≤20,m≤105n≤20,m≤10^5n≤20,m≤105

所以我们肯定先考虑 nnn

我们可以先想暴力的做法,因为最多只有 n≤20n\le 20n≤20 行,也就是最多只有 2202^{20}220 种状态,我们就先可以 2n2^n2n 暴力枚举所有的状态(表示
的实际意义就是当前状态是一个二进制数 xxx 表示 nnn 行里,每一行翻转还是不翻转),这样的话对于每一列当前的状态我们都可以
得到,我们只需要再考虑每一列翻转还是不翻转就行了。所以我们对于当前枚举到的行的状态再枚举每一列,ansansans 加上这一列里当前状态的 min{num0,num1}min\{num_0,num_1\}min{num0​,num1​}

注意本文一共出现了两种状态:

一个是行翻转状态,代表 nnn 行,每一行翻转为 111,不翻转为 000,状压之后是一个 nnn 位二进制数 ,共有 2n2^n2n 种。

一个是列状态,尽管一共有 m≤105m\le 10^{5}m≤105 列,但是每列只有 nnn 行,同样最多只有 2n2^n2n 种情况。

我们用 XiX_iXi​ 表示 2n2^n2n 次枚举,枚举到的行翻转状态,每一列的状态实际上就是这一列的 nnn 行表示的是啥, XiX_iXi​ 就意味着这一列的这 nnn 行将要进行什么样的翻转。

对于任意一列,第 iii 列的状态为 SiS_iSi​ 。

则第 iii 列最后经翻转后实际的状态 Yi=Si⊕XiY_i=S_i\oplus X_iYi​=Si​⊕Xi​。

由于每一列都可以自由翻转,那么它翻转还是不翻转取决于这一列的 min{num0,num1}min\{num_0,num_1\}min{num0​,num1​}。取 000 的话实际意义就是把这一行翻转成 111 嘛,题目想要求的是最少数目的 111 所以取 minminmin ,这样就贪心地 O(1)O(1)O(1) 完成了列的翻转

因此我们设 b[x]b[x]b[x] 表示列状态(n行) xxx 的 0/10/10/1 的较小值即 min{num0,num1}min\{num_0,num_1\}min{num0​,num1​}

设当前枚举到的行翻转状态为 nownownow,则此时的答案为

ans=∑i=1mb[i⊕now]ans=\sum_{i=1}^{m}b[i\oplus now] ans=i=1∑m​b[i⊕now]
我们枚举 2n2^n2n 个 nownownow 答案取最小值即可。

总时间复杂度为 O(m×2n)\mathcal O(m\times 2^n)O(m×2n),而 n≤10,m≤105n\le 10,m\le10^5n≤10,m≤105,肯定是不可行的。

所以考虑优化:

状压枚举行状态是没什么问题, 2202^{20}220 的复杂度非常的美丽,所以考虑如何优化枚举每一列的这个操作。

发现尽管一共有 mmm 列,每列是 nnn 行,每列的的列状态是一个 nnn 位二进制数,也就意味着一共就只有 2n2^{n}2n 种列状态,我们设 a[x]a[x]a[x] 表示列状态为 xxx 的出现次数,b[x]b[x]b[x] 表示列状态 xxx 的 0/10/10/1 的较小值即 min{num0,num1}min\{num_0,num_1\}min{num0​,num1​} ,也就是一列的状态为 xxx 的时候对答案的贡献。

我们同样 2n2^{n}2n 枚举行的翻转状态。

假设当前的行翻转状态为 nownownow ,我们枚举所有可能出现的列状态,一共只有 2n2^n2n 种,因为我们统计过了每种状态出现次数,所以如果没有这个状态,a[i]=0a[i]=0a[i]=0 ,这种行翻转状态 nownownow 的答案为:
ans=∑i=02na[i]×b[i⊕now]ans=\sum_{i=0}^{2^n} a[i]\times b[i ⊕ now] ans=i=0∑2n​a[i]×b[i⊕now]
有点 FWT 那味了hhh,因为 i⊕now=j,i⊕j=nowi\oplus now=j,i\oplus j=nowi⊕now=j,i⊕j=now,所以我们可以转一个形式:

设 f[now]f[now]f[now] 表示行翻转状态为 nownownow 时候的答案,所以有:

f[now]=∑i⊕j=nowa[i]×b[j]f[now]=\sum_{i⊕j=now}a[i]\times b[j] f[now]=i⊕j=now∑​a[i]×b[j]

发现这就是 FWT 的模板,直接卷就完了。

复杂度 O(2n×log22n)=O(n×2n)O(2^n\times log_22^n)=O(n\times 2^n)O(2n×log2​2n)=O(n×2n),n=20n=20n=20。完美 ~

Code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>using namespace std;
typedef long long ll;
typedef int itn;
typedef unsigned long long ull;
const int N = (1 << 20) + 7, M = 1e5+ 7, mod = 1e9 + 7;
const ll INF = 4e18;
itn dcnt;
#define de(x) cout << x << " x" << endl
#define de1() cout << ++ dcnt << "ok" << endl
itn t;
int n, m;
ll a[N], b[N], f[N], S[N], num[N];
char s[N];void XOR(ll *f, int n, int x = 1)
{for(int o = 2; o <= n; o <<= 1) {for(int i = 0, k = o >> 1; i < n; i += o) {for(int j = 0; j < k; ++ j) {ll X = f[i + j];ll Y = f[i + j + k];f[i + j] = X + Y;f[i + j + k] = X - Y;if(x == -1) {f[i + j] = (X + Y) / 2;f[i + j + k] = (X - Y) / 2;}}}}
}int main()
{scanf("%d%d", &n, &m);for(int i = 1; i <= n; ++ i) {scanf("%s", s + 1);for(int j = 1; j <= m; ++ j) {if(s[j] == '1')S[j] |= 1 << i - 1;}//S 是列状态}for(int i = 1; i <= m; ++ i)a[S[i]] ++ ;for(int i = 0; i < (1 << n); ++ i)num[i] = num[i >> 1] + (i & 1);for(int i = 0; i < (1 << n); ++ i)b[i] = min(num[i], n - num[i]);XOR(a, 1 << n), XOR(b, 1 << n);for(int i = 0; i < (1 << n); ++ i)f[i] = a[i] * b[i];XOR(f, 1 << n, -1);ll ans = INF;for(int i = 0; i < (1 << n); ++ i)ans = min(ans, f[i]);printf("%lld\n", ans);return 0;
}

附官方题解的神仙DP:


#include<bits/stdc++.h>using namespace std;char s[100000];
int col[100000], dp[21][1 << 20];int main() {int n, m;scanf("%d %d", &n, &m);for (int i = 0; i < n; i++) {scanf(" %s", s);for (int j = 0; j < m; j++) col[j] |= (s[j] - '0') << i;}for (int i = 0; i < m; i++) dp[0][col[i]]++;for (int k = 1; k <= n; k++)for (int mask = 0; mask < (1 << n); mask++) {int sum = k > 1 ? (k - 2 - n) * dp[k - 2][mask] : 0;for (int p = 0; p < n; p++) sum += dp[k - 1][mask ^ (1 << p)];dp[k][mask] = sum / k;}int ans = n * m;for (int mask = 0; mask < (1 << n); mask++) {int cnt = 0;for (int k = 0; k <= n; k++) cnt += min(k, n - k) * dp[k][mask];ans = min(ans, cnt);}printf("%d\\n", ans);return 0;
}

解题报告(一)D、(CROC 2016 - Final Round C)Binary Table(矩阵 + 状态压缩 + FWT)(3.5)相关推荐

  1. 解题报告(一)E、(BZOJ4589)Hard Nim(博弈论 + FWT)

    繁凡出品的全新系列:解题报告系列 -- 超高质量算法题单,配套我写的超高质量题解和代码,题目难度不一定按照题号排序,我会在每道题后面加上题目难度指数(1∼51 \sim 51∼5),以模板题难度 11 ...

  2. 8VC Venture Cup 2016 - Final Round (Div. 2 Edition) C. XOR Equation 数学

    C. XOR Equation 题目连接: http://www.codeforces.com/contest/635/problem/C Description Two positive integ ...

  3. CROC 2016 - Elimination Round (Rated Unofficial Edition) B. Mischievous Mess Makers 贪心

    B. Mischievous Mess Makers 题目连接: http://www.codeforces.com/contest/655/problem/B Description It is a ...

  4. 解题报告(一)快速沃尔什变换FWT(ACM / OI)超高质量题解

    整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 繁凡出品的全新系列:解题报告系列 -- 超高质量算法题单,配套我写的超高质量的题解和代码,题目难度不一 ...

  5. Codeforces Round #700 (Div. 2)A~D2解题报告

    Codeforces Round #700 (Div. 2)A~D2解题报告 A Yet Another String Game 原题链接 http://codeforces.com/contest/ ...

  6. Codeforces Round #693 (Div. 3)A~G解题报告

    Codeforces Round #693 (Div. 3)A~G解题报告 A Cards for Friends 原题信息 http://codeforces.com/contest/1472/pr ...

  7. Codeforces Round #697 (Div. 3)A~G解题报告

    Codeforces Round #697 (Div. 3)A~G解题报告 题 A Odd Divisor 题目介绍 解题思路 乍一想本题,感觉有点迷迷糊糊,但是证难则反,直接考虑没有奇数因子的情况, ...

  8. Codeforces Round #698 (Div. 2) A-E解题报告与解法证明

    Codeforces Round #698 (Div. 2) A-E解题报告与解法证明 题目解法总体概括 A Nezzar and Colorful Balls #include <bits/s ...

  9. 【解题报告】CF DIV3 #ROUND 734 A~D1

    [解题报告]CF DIV2 #ROUND 707 A~D 比赛链接 比赛评价: 一般性,有段时间没打了,甚至忘记多组输入hh.顺便吐槽一下翻译软件确实不行,以后还是直接看英文好了 A. Polycar ...

最新文章

  1. table_line
  2. 使用钉钉接收gitlab仓库的推送消息
  3. 桌面在计算机哪个文件夹,windows的桌面文件夹是哪个?
  4. android 八核手机,八核手机
  5. 计算机怎样旋转桌面,win7电脑怎么设置翻转屏幕
  6. 你知道几句?Linux之父十大名言
  7. 报考PMP需要准备的资料有哪些?
  8. SM3国密加密算法(C语言)
  9. Python功能实现:为pdf电子书籍生成书签目录
  10. python 相关性检验怎么计算p值_数据分析---用Python进行相关性分析(兼谈假设检验)...
  11. 百度——测试开发实习生面试记录
  12. 客户案例:Coremail安全海外中继保障德赛集团跨境通邮安全
  13. Linux实训项目——第十一章:基础DNS服务器与主从同步
  14. 4.3 CPU性能侦测
  15. 深圳求职指南(2004版)
  16. 线索二叉树的线索化、及遍历
  17. java csrf_java – 如何在使用CSRF登录后启用Spring Secu...
  18. 多波速3D双体水文测绘无人船,无人测绘船,水下地形测绘无人船
  19. 基于Unity的HTC Vive虚拟现实交互开发(一)环境配置
  20. 时序逻辑电路的设计(二) -- 篮球比赛24秒倒计时电路(附Multisim)

热门文章

  1. 为什么模型复杂度增加时,模型预测的方差会增大,偏差会减小?
  2. 【小白学PyTorch】10.pytorch常见运算详解
  3. 实战:使用Mask-RCNN的停车位检测
  4. 使用傅里叶变换进行图像边缘检测
  5. mysql 允许远程登录
  6. .NET/C#中对自定义对象集合进行自定义排序的方法
  7. 密码学===公钥和私钥解释
  8. mac上使用crontab周期性执行python脚本
  9. JQuery Datatable用法
  10. 用VS2005打开一个.NET2.0方案,里面有几个工程和一个网站,提示网站的项目.csproj文件无法打开:“此安装不支持该项目类型”,的解决办法。...