E-min of restricted sum
题目链接:https://atcoder.jp/contests/abc396/tasks/abc396_e
题意:
给定x,y,z序列要求构造一个a序列使a[xi]^a[yi]=z[i]对于每个i都成立且a的元素相加尽量小
思路:
将每个xi和yi当作两个结点,在两者间构建无向边,权值为zi
那么我们就发现这些xi和yi的关系是一些连通块(从起点出发确定块内所有点)
我们先固定一个连通块的一个起点,假设它为u,有两条边的关系:u - >v,u -> r- >t -> v
递推可得
第一条:a[xu] ^ a[xv] =z [uv] -> a[xu] =a[xv] ^z[uv]
第二条:a[xu] = z[ur]^ z[rt]^ a[xv]
因此发现为了不矛盾,我们需要使从起点出发到达的点 所经过路径异或和 一致
所以当再次搜索到了某个点只需与之前路径异或和进行比较就可以,若相同则不矛盾
为了构造使得sum【a】最小,我们把ai的值拆成二进制来分析
(需要先确定一点,然后连通块其他的点就确定了)
那么对于起点ai,令其大小为k,那么其他节点的大小= ai^路径异或和w
对于每一位,如果这些路径异或和w 1的个数多,说明ai这一位取1比较划算(因为异或),同理0个数多,就取0
最后对于一个连通块确定起点进行dfs就可以确定其他点
题目应该给的是多个连通块,这点要注意
还有要开ll
#include<bits/stdc++.h>
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define pb push_back
#define endl "\n"
#define fi first
#define int long long
#define se second
//#pragma GCC optimize(3)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef __int128 lll;
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f;
const ll llmax=LLONG_MAX;
const int maxn=2e5+5;
const int mod=1e9+7;
struct edge{
int to;int w;
};
vector<edge>g[maxn];
int vis[maxn];
int val[maxn];
int cnt;
int a[maxn];
vector<int>bt(33);
void dfs(int u,int cur,int fa){
cnt++;
vis[u]=1;
val[u]=cur;
for(int i=0;i<32;i++){
if(cur&(1<<i)){
bt[i]++;
}
}
for(auto ed:g[u]){
int v=ed.to,w=ed.w;
if(v!=fa){
if(vis[v]){
if(val[v]!=(cur^w)){
cout<<-1<<endl;
exit(0);
}
}else{
dfs(v,cur^w,u);
}
}
}
}
void dfs2(int u){
vis[u]=1;
for(auto ed:g[u]){
int v=ed.to,w=ed.w;
if(!vis[v]){
a[v]=a[u]^w;
dfs2(v);
}
}
}
void solve(){
int n,m;cin>>n>>m;
for(int i=1;i<=m;i++){
int x,y,z;cin>>x>>y>>z;
g[x].pb({y,z});
g[y].pb({x,z});
}
for(int i=1;i<=n;i++){
if(!vis[i]){
cnt=0;
dfs(i,0,0);
for(int j=0;j<32;j++){
if(bt[j]>=cnt-bt[j]){
a[i]=a[i]|(1<<j);
}
bt[j]=0;
}
}
}
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)if(!vis[i]){
dfs2(i);
}
for(int i=1;i<=n;i++)cout<<a[i]<<' ';
}
signed main()
{
ios::sync_with_stdio(false),cin.tie(0);
int T=1;
while(T--){
solve();
}
return 0;
}

浙公网安备 33010602011771号