Codeforces Round #851 (Div. 2)-F. XOR, Tree, and Queries-树、异或、并查集
题目:https://codeforces.com/contest/1788/problem/F
题解:
(首先他和线性基没什么瓜系)
想这个问题大概可以分成几个部分:
- 1、很自然地考虑记\(p_x\)表示从根节点走到x路径上边的异或和,那么每个约数:u到v的路径异或和恰为x,可以表示为\(p_u\oplus p_v\oplus p_{lca}\oplus p_{lca}=p_u\oplus p_v =x\),一定要注意LCA需要扣掉两次,这很关键。
- 2、考虑\(p_u\oplus p_v=x\)如何表示,可以建另一张图\(G_2\),在这张图上\((u,v)\)的边权x就表示\(p_u\oplus p_v\)恰好为x。
- 3、如何check是否合法?由于会由\(G_2\)若干连通块组成,每个连通块相互独立,对每个连通块任取一个节点开始dfs,给块内每个点附一个符合约束的值,因为异或和其实就只是一个相对关系,所以一旦出现某个\((u,v),(v,w)\)的边权异或不等于\((u,w)\)的边权时就无解,否则一定有解
- 4、如何保证最后的异或和最小?这里再去考虑边权异或如何用\(p\)数组表示,很容易发现所有奇度点对应的p的异或和就是所有边权的异或(而偶度点总会被重复计算),所以答案就很明确了:我们先把所有奇度点的p值全部异或起来,得到一个sum,然后再去寻找有没有哪个连通块恰有奇数个奇度点,如果有的话就给这个连通块的p全部异或上sum,这样就能得到0的答案;否则如果找不到这样的连通块,sum自然就是唯一解了。
连通块以及点的个数
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define endl '\n'
#define fastio ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define mp make_pair
typedef long long ll;
using namespace std;
const int N=250005;
int n,q,a[N],p[N],deg[N],fa[N],cnt[N],vis[N];
vector<vector<pair<int,int>>> G1,G2;
int find(int x){
if(fa[x]==x)return x;
return fa[x]=find(fa[x]);
}
void merge(int x,int y){
int fx=find(x),fy=find(y);
if(fx==fy)return;
fa[fy]=fx;cnt[fx]^=cnt[fy];
}
void dfs(int x,int d){
vis[x]=1;p[x]=d;
for(auto itr:G2[x]){
int to=itr.first;
if(!vis[to])dfs(to,d^itr.second);
}
}
void dfs2(int x,int fa){
for(auto itr:G1[x]){
int to=itr.first;
if(to==fa)continue;
dfs2(to,x);
a[itr.second]=(p[x]^p[to]);
}
}
int main(){
fastio;
cin>>n>>q;
G1.resize(n+1);G2.resize(n+1);
rep(i,1,n)fa[i]=i;
rep(i,1,n-1){
int u,v;cin>>u>>v;
G1[u].push_back(mp(v,i));
G1[v].push_back(mp(u,i));
deg[u]^=1;deg[v]^=1;
}
rep(i,1,n)cnt[i]=deg[i];
while(q--){
int u,v,x;cin>>u>>v>>x;
G2[u].push_back(mp(v,x));
G2[v].push_back(mp(u,x));
merge(u,v);
}
rep(i,1,n)if(!vis[i])dfs(i,0);
bool ok=1;
rep(i,1,n)for(auto itr:G2[i]){
int to=itr.first,w=itr.second;
if((p[i]^p[to])!=w)ok=0;
}
if(!ok)cout<<"No"<<endl;
else{
cout<<"Yes"<<endl;
int sum=0;
rep(i,1,n)if(deg[i]==1)sum^=p[i];
rep(i,1,n)if(cnt[find(i)]==1){
int x=find(i);
rep(j,1,n)if(find(j)==x)p[j]^=sum;
break;
}
dfs2(1,-1);
rep(i,1,n-1)cout<<a[i]<<' ';
}
return 0;
}