题解:CF1977D XORificator
题目链接:link。
题目大概其实就是想让我们通过翻转某些行,使得尽可能多的列成为特殊列。
众所周知,暴力肯定是不行的,所以我们需要考虑优化!
对于每一列 \(j\),枚举每一行 \(i\),通过翻转某些行使得第 \(j\) 列第 \(i\) 行为 \(1\),其余行为 \(0\)。
我们可以用哈希表记录每种翻转能够使多少列成为特殊列,最后选择最高的方案。
但h是,又众所周知哈希会出现哈希冲突所以我们还需要优化哈希!
我们用双哈希来存储翻转方案,来避免哈希冲突。并且可以预处理哈希值,这样可以更快!
总时间复杂度为 \(O(nm\times \log (nm))\),轻松卡过本题数据。
代码实现:
还有不懂得看代码吧!
#include<bits/stdc++.h>
#define I using
#define AK namespace
#define IOI std
#define i_ak return
#define ioi 0
#define i_will signed
#define ak main
#define IMO ()
#define int long long
I AK IOI;
const int N=3e5+5,P=13331,M1=1e9+7,M2=998244353;
string s[N];//存储矩阵的每一行
int p1[N],p2[N];
void solve(){
int n,m;
cin>>n>>m;
for(int i=0;i<n;i++)cin>>s[i];//输入矩阵的每一行
//预处理哈希幂次
p1[0]=p2[0]=1;
for(int i=1;i<n;i++){
p1[i]=1ll*p1[i-1]*P%M1;
p2[i]=1ll*p2[i-1]*P%M2;
}
//哈希表,记录每种翻转方案的频率
map<array<int,2>,int>mp;
//枚举每一列
for(int j=0;j<m;j++){
//计算使得第 j 列全部变成 0 的操作序列的哈希值
int h1=0,h2=0;
for(int i=0;i<n;i++){
h1=(1ll*h1*P+s[i][j])%M1;
h2=(1ll*h2*P+s[i][j])%M2;
}
//枚举每一行,计算使得第 j列的第 i 行为 1 的操作序列的哈希值
for(int i=0;i<n;i++){
array<int,2>t;
t[0]=(h1+1ll*((s[i][j]^1)-s[i][j]+M1)*p1[n-1-i])%M1;
t[1]=(h2+1ll*((s[i][j]^1)-s[i][j]+M2)*p2[n-1-i])%M2;
mp[t]++;//更新哈希表的频率
}
}
//找到最大频率
int ret=0;
for(auto&it:mp)ret=max(ret,it.second);
cout<<ret<<'\n';//输出最大特殊列数
//找到对应的翻转方案
for(int j=0;j<m;j++){
int h1=0,h2=0;
for(int i=0;i<n;i++){
h1=(1ll*h1*P+s[i][j])%M1;
h2=(1ll*h2*P+s[i][j])%M2;
}
for(int i=0;i<n;i++){
array<int,2>t;
t[0]=(h1+1ll*((s[i][j]^1)-s[i][j]+M1)*p1[n-1-i])%M1;
t[1]=(h2+1ll*((s[i][j]^1)-s[i][j]+M2)*p2[n-1-i])%M2;
if(mp[t]==ret){//如果找到最大频率对应方案
s[i][j]^=1;//翻转第 i 行
for(int i=0;i<n;i++)cout<<s[i][j];//输出翻转方案
puts("");
return;
}
}
}
}
i_will ak IMO{
int t;
cin>>t;
while(t--){
solve();
}
i_ak ioi;
}
亲测可过,请勿抄袭!

浙公网安备 33010602011771号