SRM 590 DIV1 B题 XorCards ---- 高斯消元法

很久就开通博客了  无奈人太懒  从来没写过  最近开始学linux  开始慢慢写linux日志了  就顺带着也写点ACM的题解吧


题意:给你n个数a0,a1...a(n-1)(n<=50,ai<=1e15) 和一个数lim(lim<=1e15),让你从n个数中任意选出一些数,使得这些数的xor值小于等于lim,求总的方案数

例如:  {1,2} 3 ---> 答案为4  选择方案有 {}(一个都不选) {1} {2} {1,2}

           {5,5} 2 ---> 答案为2  方法有 {} , {5,5}

           {5,5,5} 2 --- > 答案为 4  方法有 {},{5,5},{5,5},{5,5}    // 可把 2 3 4个解分别看为 {a0,a1} {a0,a2} {a1,a2} 视为不同解


思路:先将lim拆分为二进制,假如lim拆分过后为 110010,那么我们可以这样来求

先求a0~a(n-1) 组成 0xxxxx 这种形式的方案数, 其中x表示该位为0或1皆可

再求10xxxx,以此类推,在分别求11000x,110010的方案数

之后把前面求出的值累加即为 最后 答案


求各种情况的方案数需要用到 高斯消元法

具体怎么求呢

假设不存在未知数x ,那么直接以数列a和lim建立系数矩阵 , 然后化简后求出自由元个数,则此方案数即是 (1<<自由元个数)

再来看有未知数x的情况

先假设 n=2 其中 a0 = 010111 , a1 = 111000 , lim = 110010

现在我们求 10xxxx这种情况下的解

我们可以将 令 lim_b = 100000 , b0 = 010000 , b1 = 110000,即使将 a和lim 后面在未知数x中的数字变为0 得到 lim_b和 数列b

再以数列b和lim_b建立系数矩阵,然后求出自由元个数xb,则10xxxx的解个数即为 (1<<xb)   

//  这样做可行的原理也很简单,因为未知数x之前的数已经小于lim了,所以不论后面的数字如何组合,也不会让 其XOR值>lim,即后面随意怎么

// 选择搭配都可以,既然如此,就索性把后面包含在未知数x内的元素全部变成0来计算。(本人也是在看了别人的代码后才发现能够这样搞 = =)


下面上代码

#include <iostream>
#include <cstring>
#include <stdio.h>
#include <math.h>
#include <fstream>
#include <algorithm>
#include <stack>
#include <vector>
#include <queue>
using namespace std;

#define REP(i,n) for(int i=0;i<(n);i++)
#define FOR(i,j,k) for(int i=j;i<=(k);i++)
#define ll long long

ll Solve(vector <ll> num,ll lim){
    // 构建系数矩阵,我习惯先把每个数横向构造后,再将矩阵转置
    int n = num.size(),m = 52;
    bool a[55][55];
    memset(a,0,sizeof(a));
    REP(i,n) REP(j,m)
        if(num[i]&(1ll<<j)) a[i][j] = 1;
    REP(j,m) if(lim&(1ll<<j)) a[n][j] = 1;
    REP(i,55) REP(j,i) swap(a[i][j],a[j][i]);   // 转置
    swap(n,m);  // 转置矩阵后交换 行列数

    int top = 0;
    REP(j,m) {  // 化简矩阵
        int tmp = 0;
        FOR(i,top,n) if(a[i][j]){
            REP(k,m+1)    swap(a[top][k],a[i][k]);
            tmp = 1;break;
        }
        FOR(i,top+1,n) if(a[i][j])
            REP(k,m+1) a[i][k] ^= a[top][k];
        top += tmp;
    }
    REP(i,n) if(a[i][m]){   // 判断是否无解
        bool flag = 0;
        REP(j,m)    if(a[i][j]) flag = 1;
        if(!flag)   return 0;
    }
    return (1ll<<(m-top)); // 自由元个数即为 未知数个数-主元列数(m-top)
}

class XorCards{
public:
    long long numberOfWays(vector<long long> number, long long limit){
        vector <ll> a = number;
        ll b = limit;
        ll res = Solve(a,b);
        REP(i,50) if(limit&(1ll<<i)){
            // 下面两句将 包含在未知数x 内的数变为0
            b = ((limit>>(i+1)) << (i+1));
            REP(j,a.size()) a[j] = ((number[j]>>i) << i);
            res += Solve(a,b);
        }
        return res;
    }
};


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值