[高斯消元与线性基]

[BZOJ 2115][WC 2011]Xor

题意

  • 给定一个n个点m条边的无向图,求一条路径使得路径的异或和最大

分析

  • 随便搞出一条路径
  • 考虑环,路径^环=路径,相当于我们从环的另一侧走过去,考虑两条路径,路径^路径=环
  • 然后找到图中所有的环分别求xor,最后判定取哪些环能使答案最大。
  • 进行高斯消元,用拟阵证明取线性基的正确性

Code

#include 
using namespace std;
#define N 50010
#define M 200010
typedef long long ll;
struct Edge{
    int to, next;
    ll w;
}edge[M];

int cnt, h[N], n, m;

void add(int u, int v, ll w){
    cnt ++;
    edge[cnt].to = v;
    edge[cnt].w = w;
    edge[cnt].next = h[u];
    h[u] = cnt;
    swap(u, v);
    cnt ++;
    edge[cnt].to = v;
    edge[cnt].w = w;
    edge[cnt].next = h[u];
    h[u] = cnt;
}

ll d[N], a[M], num;

bool vis[N];

void dfs(int x){
    vis[x] = 1;
    for(int i = h[x]; i; i = edge[i].next){
        int y = edge[i].to;
        if(!vis[y]) d[y] = d[x] ^ edge[i].w, dfs(y);
        else a[++ num] = d[y] ^ d[x] ^ edge[i].w;
    }
}

int gauss(){
    int k = 1;
    for(int p = 63; p >= 0; p --){
        int t = 0;
        for(int i = k; i <= num; i ++)
            if(a[i] >> p & 1){t = i; break;}
        if(t){
            swap(a[t], a[k]);
            for(int i = 1; i <= num; i ++)
                if(i != k && (a[i] >> p & 1))
                    a[i] ^= a[k];
            k ++;
        }
    }
    return k - 1;
}

int main(){
    scanf("%d%d", &n, &m);
    int u, v; ll w;
    for(int i = 1; i <= m; i ++){
        scanf("%d%d%lld", &u, &v, &w);
        add(u, v, w);
    }
    dfs(1);
    int cnt = gauss();
    ll ans = d[n];
    for(int i = 1; i <= cnt; i ++)
        if((ans ^ a[i]) > ans)
            ans ^= a[i];
    printf("%lld\n", ans);
    return 0;
}

[BZOJ 2460][BeiJing2011]元素

题意:

  • 给定一些元素,每个元素有两个值a和b,现在需要选出一些元素,在不存在a值异或和为0的子集的情况下使b之和最大

分析

  • 贪心(拟阵)+线性基
#include 
#define maxn 1010
using namespace std;
typedef long long ll;
struct Point{
    ll a;
    int b;
    bool operator<(const Point& k)const{
        return b > k.b;
    }
}p[maxn];

int n;

ll ans, base[maxn];

int main(){
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++)
        scanf("%lld%d", &p[i].a, &p[i].b);
    sort(p+1, p+1+n);
    for(int i = 1; i <= n; i ++){
        for(int j = 63; ~j; j --){
            if(p[i].a >> j & 1){
                if(!base[j]){
                    base[j] = p[i].a;
                    break;
                }
                p[i].a ^= base[j];
            }
        }
        if(p[i].a)ans += p[i].b;
    }
    printf("%lld\n", ans);
    return 0;
}

[BZOJ 4004][JLOI2015]装备购买

题意:

  • 有一个人买一些装备,要求线性无关,求买装备最多而且花费最少

分析:

  • 拟阵证明贪心的正确性
  • 用高斯消元维护线性基
#include 
#define maxn 510
using namespace std;
typedef long long ll;
//const int mod = 998224353;
const int mod = 1e9 + 7;
struct Node{
    int a[maxn], c;
    bool operator<(const Node& k)const{return c < k.c;}
}p[maxn], bases[maxn];
bool bases_flag[maxn];
int n, m;

ll power_mod(ll a, ll b, ll mod){
    ll ret = 1;
    while(b){
        if(b & 1)ret = ret * a % mod;
        b >>= 1;
        a = a * a % mod;
    }return ret;
}

void Gauss(Node& a, const Node& b, int o){
    //t = a.a[o] / b.b[o]
    ll t = mod - a.a[o] * power_mod(b.a[o], mod-2, mod) % mod;
    for(int i = o; i <= m; i ++)
        a.a[i] =(a.a[i] + b.a[i] * t) % mod;
}

bool Insert(int pos){
    for(int i = 1; i <= m; i ++){
        if(p[pos].a[i]){
            if(!bases_flag[i]){
                bases[i] = p[pos];
                bases_flag[i] = true;
                return true;
            }
            Gauss(p[pos], bases[i], i);
        }
    }
    return false;
}

int main(){
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= m; j ++)
            scanf("%d", &p[i].a[j]);

    for(int i = 1; i <= n; i ++)
        scanf("%d", &p[i].c);

    sort(p+1, p+1+n);

    long long ans = 0;
    int cnt = 0;
    for(int i = 1; i <= n; i ++)
        if(Insert(i))ans += p[i].c, cnt ++;
    printf("%d %lld\n", cnt, ans);
    return 0;
}

你可能感兴趣的:(BZOJ,入门--,奇奇怪怪的东西,数学--高斯消元。线性基)