【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\)

则有:

\[\begin{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)=0\end{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;//能加就加
}
posted @ 2020-01-28 21:05  TheLostWeak  阅读(...)  评论(...编辑  收藏