1. 程式人生 > >線性基 模板 bzoj2460 【BeiJing2011】 元素

線性基 模板 bzoj2460 【BeiJing2011】 元素

題目大意:
有許多礦石,礦石有編號和價值兩種屬性,我們要求一個集合,使得該集合任意子集編號異或和不為0,並且要求價值和儘量大。

題目分析:
先科普線性基。
線性基是我們構造出一個集合,這個集合中的所有元素能相互異或能得到原集合中任意子集異抑或和。
當然我們構造出的線性基是具有一些優秀性質的。
線性基中的元素只有二進位制位數個。
其中第i個元素ins[i]代表用所有數能異或出的最高位為第i位的數。

構造方法:
對於一個數,從高往低對於每一個二進位制位判斷。
如果這一位是1:
如果線性基中最高位為這一位的數為0,那就把這個數加進線性基,並跳出掃描,否則就把這個數異或上線性基中這一位的元素(相當於把這一位消去),然後比較下一位。
如果這一位是0:
比較下一位。

這道題可以貪心的把價值按照從大到小排序,然後掃描一遍,如果當前的礦石編號不能被前面所有的礦石異或出來的話,就選進來。

這樣的話在構造線性基的時候就可以直接判斷出來了。
構造線性基的時候,如果這個數不能加進線性基裡,即最後被異或成0,那就說明這個數能被前面的數異或出來,就不能加進答案。

程式碼如下:

#include <cstdio>
#include <algorithm>
#define N 1005
using namespace std;
typedef long long LL;
struct Node{
    int x;
    LL val;
    bool
operator < (const Node &c) const {return x>c.x;} }a[N]; LL ins[66],ans; int n; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%lld%d",&a[i].val,&a[i].x); sort(a+1,a+1+n); for(int i=1;i<=n;i++) { for(int k=65;k>=0;k--) { if
(a[i].val>>k) { if(!ins[k]){ins[k]=a[i].val; break;} a[i].val^=ins[k]; } } if(a[i].val) ans+=a[i].x; } printf("%lld\n",ans); return 0; }