2024-04-08

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 了
这其实是个乱搞做法,但是数据太水就过了

为什么是错的呢?

看样例:
排完序之后是

\[1 1 0 1\newline 0 0 1 0 \]

线性基中存的是

\[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

逆天题想了一晚上想不明白我太废物了快崩溃了马滴

posted @ 2024-04-08 21:37  OrangeStar*  阅读(5)  评论(0编辑  收藏  举报