题解:P15877 [ICPC 2026 NAC] Cable Pruning
来一个线性做法。
首先考虑子树内怎么做,显然路径上的点是一定要保留的,那么怎么判断一条边是否要保留?显然可以路径加,可以树上差分维护,但是这样太劣了,考虑更优,两个点异或上一个随机权值,那么就变成了子树异或,这样是
到了环上,注意到如果一段区间内异或和为
记子树的答案为
维护前缀子树异或和,记
总复杂度
:::success[code]
#include<bits/stdc++.h>
#define ull unsigned long long
#define int long long
using namespace std;
const int N=1e5+7;
int n,m;
vector<pair<int,int>>edge[N];
int st[N],top;
int vis[N];
vector<int>clr;
inline void dfs(int x,int lst)
{
if(clr.size())return;
if(vis[x])
{
for(int i=top;i;i--)
{
clr.push_back(st[i]);
if(st[i]==x)break;
}
return;
}
vis[x]=1;
st[++top]=x;
for(auto [v,w]:edge[x])
{
if(v==lst)continue;
dfs(v,x);
if(clr.size())return;
}
top--;
}
int fa[N];
inline void dfs1(int x,int rt)
{
vis[x]=rt;
for(auto [v,w]:edge[x])
{
if(vis[v]!=0||v==fa[x])continue;
fa[v]=x;
dfs1(v,rt);
}
}
mt19937_64 rnd(time(0));
ull sum[N];
int ans;
inline void dfs2(int x,int rt)
{
for(auto [v,w]:edge[x])
{
if(vis[v]!=rt||v==fa[x])continue;
dfs2(v,rt);
sum[x]^=sum[v];
}
}
inline void dfs3(int x,int rt)
{
for(auto [v,w]:edge[x])
{
if(vis[v]!=rt||v==fa[x])continue;
if(sum[v])ans+=w;
dfs3(v,rt);
}
}
int a[N],b[N];
unordered_map<ull,int>mp;
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1,x,y,w;i<=n;i++)
{
cin>>x>>y>>w;
edge[x].push_back({y,w});
edge[y].push_back({x,w});
}
dfs(1,0);
for(int i=1;i<=n;i++)vis[i]=0;
for(int v:clr)vis[v]=1;
for(int v:clr)dfs1(v,v);
for(int i=1;i<=m;i++)
{
cin>>a[i]>>b[i];
ull w=rnd();
sum[a[i]]^=w;
sum[b[i]]^=w;
}
for(int v:clr)dfs2(v,v);
for(int v:clr)dfs3(v,v);
for(int i=1;i<=m;i++)
a[i]=vis[a[i]],b[i]=vis[b[i]];
int s=clr.size();
int tot=0,mx=0;
ull cur=0;
for(int i=0;i<s;i++)
{
int x=clr[i],y=clr[(i+1)%s];
int w=0;
for(auto [v,c]:edge[x])
{
if(v==y)
{
w=c;
break;
}
}
tot+=w;
cur^=sum[x];
mp[cur]+=w;
}
for(auto it=mp.begin();it!=mp.end();it++)
mx=max(mx,it->second);
cout<<ans+tot-mx<<'\n';
}
:::