zoukankan      html  css  js  c++  java
  • 【BZOJ2460】 [BeiJing2011] 元素(贪心+线性基)

    点此看题面

    大致题意:(n)个物品,每个物品有两个属性:编号和价值。现让你选出一个物品集合,使得其不存在某个子集编号异或和为(0),求出满足条件的集合中物品价值总和的最大值。

    前言

    (Jan 28th)刷题计划(6/6),算法标签:贪心、线性基。

    以这道有趣的贪心题结尾,终于完成了今天的任务!

    性质

    考虑已知一个合法集合({a_1,a_2,...,a_k}(Val(a_1)ge Val(a_2)ge...ge Val(a_k))),现在加进去一个新的元素(x),结果所得到的集合就不合法了。

    仔细推一推,我们可以发现一个性质:这个新集合中仅有一个子集编号异或和为(0)

    假设如果有两个子集({a_{i_1},a_{i_2},...,a_{i_{ki}},x})({a_{j_1},a_{j_2},...,a_{j_{kj}},x})编号异或和为(0)

    则有:

    [egin{cases}Pos(a_{i_1}) xor Pos(a_{i_2}) xor ... xor Pos(a_{i_{ki}}) xor Pos(x)=0\Pos(a_{j_1}) xor Pos(a_{j_2}) xor ... xor Pos(a_{j_{kj}}) xor Pos(x)=0end{cases} ]

    把两个式子合并起来,就得到:

    [Pos(a_{i_1}) xor Pos(a_{i_2}) xor ... xor Pos(a_{i_{ki}}) xor Pos(x)=Pos(a_{j_1}) xor Pos(a_{j_2}) xor ... xor Pos(a_{j_{kj}}) xor Pos(x) ]

    约掉式子两边相同的部分,假设得到一个新的等式:

    [Pos(a_{u_1}) xor Pos(a_{u_2}) xor ... xor Pos(a_{u_{ku}})=Pos(a_{v_1}) xor Pos(a_{v_2}) xor ... xor Pos(a_{v_{kv}}) ]

    其中,(u_1,u_2,...,u_{k_u},v_1,v_2,...,v_{k_v})互不相同。

    然后把这个等式两边同时异或上(Pos(a_{v_1}) xor Pos(a_{v_2}) xor ... xor Pos(a_{v_{kv}})),右式就变成了(0),得到:

    [Pos(a_{u_1}) xor Pos(a_{u_2}) xor ... xor Pos(a_{u_{ku}}) xor Pos(a_{v_1}) xor Pos(a_{v_2}) xor ... xor Pos(a_{v_{kv}})=0 ]

    也就是说,子集({u_1,u_2,...,u_{k_u},v_1,v_2,...,v_{k_v}})的编号异或和为(0),这与({a_1,a_2,...,a_k})是合法的矛盾。

    所以,这一性质得证。

    利用性质

    我们假设这个子集为({a_{i_1},a_{i_2},...,a_{i_{ki}},x})

    如果(Val(a_{i_{ki}})ge Val(x)),那么(x)的加入完全是没有意义的。

    否则,(Val(a_{i_{ki}})<Val(x))

    考虑如果我们用(x)去替换(a_{ki}),由于:

    [Pos(a_{i_1}) xor Pos(a_{i_2}) xor ... xor Pos(a_{i_{ki}}) xor Pos(x)=0 ]

    所以:

    [Pos(a_{i_1}) xor Pos(a_{i_2}) xor ... xor Pos(a_{i_{ki-1}}) xor Pos(x)=Pos(a_{i_{ki}})≠0 ]

    而根据我们之前得到的性质,其余的子集编号异或和也皆不为(0),所以得到的这个新的集合是合法的。

    而由于(Val(x)>Val(a_{i_{ki}})),所以得到的新的集合比原集合的价值总和更大。

    贪心

    综上所述,我们可以得到一个贪心的想法:把所有物品按照价值从大到小排序,然后能加就加入集合。

    根据我们之前推出来的性质,这个想法显然是正确的。

    那么新的问题就是如何判断一个物品能否加入当前集合。

    这就需要用到线性基了。

    如果一个数字能够加入线性基,那么它就能加入当前集合,否则就不能。

    具体实现可以详见代码。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 1000
    #define LV 64
    #define LL long long
    using namespace std;
    int n;LL v[LV+5];struct Item {LL x;int y;I bool operator < (Con Item& o) Con {return y>o.y;}}s[N+5];//把物品绑成结构体便于排序
    I bool Ins(LL x) {for(RI i=LV;~i;--i) if((x>>i)&1) {if(!v[i]) return v[i]=x;else x^=v[i];}return 0;}//插入线性基
    int main()
    {
    	RI i,t=0;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%lld%d",&s[i].x,&s[i].y);
    	for(sort(s+1,s+n+1),i=1;i<=n;++i) Ins(s[i].x)&&(t+=s[i].y);return printf("%d",t),0;//能加就加
    }
    
  • 相关阅读:
    Java当中的IO
    Java当中的类集框架
    Java当中的JVM
    Java当中的常量池
    详细讲解String和StringBuffer和StringBuilder的使用
    Java中流的操作以及编码解码
    VMware-workstation12.5.6 新建虚拟机 安装 centos6.5
    linux 常用命令
    macos 下安装brew
    mysql 的一些事
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2460.html
Copyright © 2011-2022 走看看