Loading

题解: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;
} 

亲测可过,请勿抄袭!

posted @ 2025-06-09 15:47  盼满天繁星  阅读(18)  评论(0)    收藏  举报