2024-04-08
为了改昨天的 T1 ,学一下线性基
线性基
线性基:
找到一堆向量的一组基底,由原先所有向量组合出的数也可以由这组基底组合出来,且保证基底内含有的向量个数最少(极大线性无关组)
不难发现一组线性基的向量数量为向量的维数
主要用到异或线性基
构建方法是扫描每个向量 \(x\) 的所有维
如果当前第 \(i\) 维 \(x_i\) 是 1:
-
若 \(b_i\) 不存在,\(b_i=x\) 并结束扫描
-
若 \(b_i\) 存在,\(x^=b_i\) 并继续扫描
然后就可以做这道题了
因为 \(3^x>\sum_{i=0}^{n-1}3^i\) 所以我们可以贪心
按照 \(b\) 从大到小考虑
贪心的用线性基求出来 当 \(a=1\) 时尽量为 \(1\) 否则尽量为 \(0\) 这时能达到的最优结果
(这题数据太水了,小样例没过都 AC 了
这其实是个乱搞做法,但是数据太水就过了
为什么是错的呢?
看样例:
排完序之后是
线性基中存的是
\[x_1: 1 1 0 1 \newline x_2: 0 0 0 0 \newline x_3: 0 0 1 0 \newline x_4: 0 0 0 0 \]2,4 位置是空的
我们在 1,3 处想要这一位尽可能是 1
所以会异或上 \(x_1,x_3\)
ans 变为 \(1,1,1,1\)
而 2,4 刚好 a 是 -1
这导致我们身不由己得选上了 -1
错误答案是 0
实际上可以选择 ans=\(0,0,1,0\),答案为 3
正解是这个
当前后两项得 b 相同时,当能达到 1,0 ,贡献为 \(3^b\)
否则可以证明一定不能到达 0,1
而能到达的 0,0 和 1,1 两种情况的贡献都是 0
我们直接删掉
这样就对了
(感觉写的好乱……
Code: 正解
#include <iostream>
#include <cstring>
#include <algorithm>
#include <bitset>
typedef long long ll;
using namespace std;
const int N=2e5+10,M=75;
int n,m;
char str[N][M];
ll pwr3[40];
bitset<M> x[N];
bitset<M> ans;
bitset<M> bas[M];
void insert(bitset<M> w) {
for(int i=1;i<=m;i++) {
if(!w[i]) continue;
if(bas[i].none()) {
bas[i]=w;
break;
}
w^=bas[i];
}
}
struct Data {
int id;
int a,b;
bool operator< (const Data &t)const {
if(b==t.b) return a>t.a;
return b>t.b;
}
}q[N];
int main() {
freopen("in.in","r",stdin);
freopen("out.out","w",stdout);
pwr3[0]=1;
for(int i=1;i<=36;i++) pwr3[i]=pwr3[i-1]*3ll;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%s",str[i]+1);
for(int i=1;i<=m;i++) scanf("%d%d",&q[i].a,&q[i].b),q[i].id=i;
sort(q+1,q+m+1);
for(int j=1;j<=m;j++) {
int k=q[j].id;
for(int i=1;i<=n;i++) {
if(str[i][k]=='1') x[i][j]=1;
else x[i][j]=0;
}
}
for(int i=1;i<=n;i++) insert(x[i]);
int nw=1;
while(nw<=m) {
if(nw==m||q[nw].b!=q[nw+1].b) {
if(q[nw].a>0&&!ans[nw]) ans^=bas[nw];
if(q[nw].a<0&&ans[nw]) ans^=bas[nw];
nw++;
}
else {
bool f11=false,f00=false;
bitset<M> tmp=ans;
if(!tmp[nw]) tmp^=bas[nw];
if(tmp[nw+1]) tmp^=bas[nw+1];
if(tmp[nw]&&!tmp[nw+1]) {
ans=tmp;
nw+=2;
continue;
}
bitset<M> t11=ans,t00=ans;
if(!t11[nw]) t11^=bas[nw];
if(!t11[nw+1]) t11^=bas[nw+1];
if(t11[nw]&&t11[nw+1]) f11=true;
if(t00[nw]) t00^=bas[nw];
if(t00[nw+1]) t00^=bas[nw+1];
if(!t00[nw]&&!t00[nw+1]) f00=true;
if(f11&&f00) {
bas[nw][nw]=bas[nw][nw+1]=bas[nw+1][nw+1]=0;
ans=t11;
insert(bas[nw]),insert(bas[nw+1]);
}
else if(f11) ans=t11;
else if(f00) ans=t00;
nw+=2;
}
}
ll res=0;
for(int i=1;i<=m;i++) if(ans[i]) res=res+q[i].a*pwr3[q[i].b];
printf("%lld\n",res);
return 0;
}
poker
逆天题想了一晚上想不明白我太废物了快崩溃了马滴
标签:tmp,bas,04,08,t00,2024,t11,ans,nw From: https://www.cnblogs.com/Orange-Star/p/18121174