题意:
给定n个点m条边的无向图,点有点权ai,边有边权。
一个点集s的“关键边集合”定义为连接的两个顶点恰好有一个在s中的边的集合。
称一个点集s是合法的,当且仅当对于s的任何一个子集t,所有在t的关键边集合中的边的边权xor不为0。
一个点集s的价值是sigma 点i属于s ai - sigma 点i不属于s ai
求价值最大的合法点集。
其中 n ≤ 1 0 5 , m ≤ 2 ∗ 1 0 5 n\le 10^5,m\le 2*10^5 n≤105,m≤2∗105
不得不说,这个题目是真的难懂啊。
首先,我们考虑,只有哪些边会存在贡献,那就是一个端点被选择,另一个端点不被选择的边。那么如果我们通过异或来表示的话,如果这个边被异或了两次或者 0 0 0次,那么他就不会产生贡献。
那我们不妨直接令 f [ i ] f[i] f[i]表示与 i i i相邻的边的异或和,我们会发现,如果一个条边的两个端点都选,那么这个边一定不会产生贡献,也正好被异或掉。
考虑要满足任意子集的异或和都大于 0 0 0,所以要线性基,因为权值尽可能大,所以先排序再做就是对的,类似 B J W C 2011 BJWC2011 BJWC2011元素
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<set>
#include<map>
#define mk make_pair
#define pb push_back
#define ll long long
#define int long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
//做法类似luogu4570 BJWC元素
const int maxn = 4e5+1e2;
int n,m;
int base[maxn];
struct Node{
int f,val;
};
Node a[maxn];
int v[maxn],w[maxn];
int f[maxn];
int sum;
int ans;
bool cmp(Node a,Node b)
{
if (a.val==b.val) return a.f>b.f;
return a.val>b.val;
}
signed main()
{
n=read(),m=read();
for (int i=1;i<=n;i++)
{
w[i]=read();
sum+=w[i];
}
for (int i=1;i<=m;i++)
{
int u=read(),v=read(),p=read();
f[u]^=p;
f[v]^=p;
}
for (int i=1;i<=n;i++)
a[i].f=f[i],a[i].val=w[i];
sort(a+1,a+1+n,cmp);
for (int i=1;i<=n;i++)
{
int now = a[i].f;
for (int j=63;j>=0;j--)
{
if (now & (1ll << j))
{
if (base[j]==0)
{
base[j]=now;
ans+=a[i].val;
break;
}
now^=base[j];
}
}
}
//cout<<ans<<endl;
sum=sum-ans;
cout<<ans-sum<<endl;
return 0;
}