【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;//能加就加
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2460.html