题意
你有一个 n 行 m 列的 01 矩阵 A。
如果矩阵的第 i 列有奇数个 1,那么它的权值就是
a
[
i
]
3
b
[
i
]
a[i]3^{b[i]}
a[i]3b[i],否则它的权值就是 0。一
个矩阵的权值定义为每列的权值和。
现在你可以删去这个矩阵的任意多行 (可以为 0),使得矩阵的权值最大。
数据范围
对于所有数据,保证
1
≤
m
≤
70
,
a
[
i
]
=
±
1
,
1
≤
b
[
i
]
≤
35
1 ≤ m ≤ 70,a[i] = ±1,1 ≤ b[i] ≤ 35
1≤m≤70,a[i]=±1,1≤b[i]≤35,且对于任意的
i
!
=
j
i != j
i!=j,保证
a
[
i
]
!
=
a
[
j
]
a[i] != a[j]
a[i]!=a[j] 或
b
[
i
]
!
=
b
[
j
]
b[i] != b[j]
b[i]!=b[j]。
对于 30% 的数据,
1
≤
n
≤
20
;
1 ≤ n ≤ 20;
1≤n≤20;
对于 70% 的数据,
1
≤
n
≤
2000
;
1 ≤ n ≤ 2000;
1≤n≤2000;
对于 100% 的数据,
1
≤
n
≤
200000.
1 ≤ n ≤ 200000.
1≤n≤200000.
冷静分析一波可以发现
3
i
3^i
3i大于后面所有的
3
0
到
3
i
−
1
的
和
3^0到3^{i-1}的和
30到3i−1的和
所以可以逐位确定.考虑当前处理从大往小处理到
3
i
3^i
3i.
然后就是一个被重新定义了权值的线性基.因为有负数的权值,所以对于正数和负数要用不同的位置表示.
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2e5+5;
inline int read(){
char c=getchar();int t=0,f=1;
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
return t*f;
}
int pre[maxn],ans,n,m,tot;
bitset<75>a[maxn],c[maxn],b;
struct node{
int a,d,id;
}q[75];
char s[75];
bool cmp(node a,node b){
return a.d>b.d||((a.d==b.d)&&a.a>b.a);
}
int check(bitset<75> a){
int sum=0;
for(int i=0;i<m;i++)if(a[i])sum+=pre[q[i].d]*q[i].a;
return sum;
}
signed main(){
//freopen("a.in","r",stdin);
//freopen("a2.out","w",stdout);
n=read(),m=read();
for(int i=0;i<n;i++){
scanf("%s",s);
for(int j=0;j<m;j++)c[i][j]=s[j]-'0';
}
pre[0]=1;
for(int i=1;i<=35;i++)pre[i]=pre[i-1]*3;
for(int i=0;i<m;i++){q[i].a=read(),q[i].d=read();q[i].id=i;}
sort(q,q+m,cmp);
for(int i=0;i<n;i++){
for(int j=0;j<m;j++)a[i][j]=c[i][q[j].id];
}
tot=0;ans=0;
for(int j=0;j<m;j++){int i;//高消求出线性基
for(i=tot;i<n;i++)if(a[i][j])break;
if(i==n)continue;
swap(a[i],a[tot]);
for(int i=0;i<n;i++)if(i!=tot&&a[i][j])a[i]^=a[tot];
tot++;
}
bool flag=1;
while(flag){
flag=0;
for(int i=0;i<tot;i++)if(check(b^a[i])>ans){
b^=a[i];ans=check(b);flag=1;
}
}
printf("%lld\n",ans);
return 0;
}