好神仙的 IOI 题。。。这个不仅往儿子转移,而往更深层的后代转移的树形 DP 就很 nb。
首先注意到,破坏树上环显然只能破坏非树边。那么对所有偶树上环都直接要把非树边破坏掉,剩下来一些奇树上环。如果奇树上环有交,那么显然能叠成偶环;否则显然无偶环。所以我们就是要删掉一些奇树上环使剩下的所有环不交,并且最大化剩下的权值和。
最大权独立集?先不说二分图最大权独立集能不能做,首先这就不是二分图,随便三个互相重叠的环就废了。所以我们需要利用树结构的特殊性 DP 来搞。
考虑在子树 (i) 内决策 LCA 等于 (i) 的环然后转移下去。如果加入一条链的话,我们将这条链上的边全删掉,剩下来若干个连通块,除了一个其它每个连通块都是某子树删或不删某个儿子树的状态,特例是 (i) 所在连通块,是子树 (i) 删两个儿子树。它们显然是独立的(因不连通),所以可以分别求然后加起来。那么我们要记录再 (j,k) 分别表示 (i) 删除的儿子树吗?如果那样,那对于每个状态还可能再删,删到四个,永无止境地迭代下去。但注意到 (n,m,2^{deg}) 这三者都是 1e3 级别,两两相乘加起来不会炸。(deg) 就是儿子树的个数,那么我们可以对删掉的儿子树集合进行状压,这样就可以了。
转移的话分两种,一种是不取任何 LCA 等于 (i) 的环,那么就往 mask 里的儿子转移。另一种就取一个转移到自己的不同 mask 以及后代们。这样复杂度对每个环处理的时间是 (mathrm O(n)),总复杂度是 (mathrm O!left(nm2^{deg} ight)),爆炸。不过我们可以在枚举 (2^{deg}) 外面把每个环的代价预处理好,这样就把 (2^{deg}) 和 (m) 平行化掉了。
code
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
const int N=5010,LOG_N=15;
int n,m;
vector<int> nei[N];
int cx[N],cy[N],cv[N],ca[N];
int fa[N][LOG_N],dep[N];
int id[N/5][N/5];
void dfs(int x=1){
for(int i=1;i<LOG_N;i++)fa[x][i]=fa[fa[x][i-1]][i-1];
int now=0;
for(int i=0;i<nei[x].size();i++){
int y=nei[x][i];
if(y==fa[x][0])continue;
id[x][y]=now++;
fa[y][0]=x;
dep[y]=dep[x]+1;
dfs(y);
}
}
int lca(int x,int y){
if(dep[x]<dep[y])swap(x,y);
for(int i=LOG_N-1;~i;i--)if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
if(x==y)return x;
for(int i=LOG_N-1;~i;i--)if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int dp[N/5][1<<10];
vector<int> vv[N];
int add[N],msk[N];
void dfs0(int x=1){
for(int i=0;i<nei[x].size();i++){
int y=nei[x][i];
if(y==fa[x][0])continue;
dfs0(y);
}
for(int i=0;i<vv[x].size();i++){
int y=vv[x][i],u=cx[y],v=cy[y];
add[y]=cv[y];
if(u!=x){
add[y]+=dp[u][0];
while(fa[u][0]!=x)add[y]+=dp[fa[u][0]][1<<id[fa[u][0]][u]],u=fa[u][0];
msk[y]|=1<<id[x][u];
}
if(v!=x){
u=v;
add[y]+=dp[u][0];
while(fa[u][0]!=x)add[y]+=dp[fa[u][0]][1<<id[fa[u][0]][u]],u=fa[u][0];
msk[y]|=1<<id[x][u];
}
}
for(int i=(1<<nei[x].size()-(x!=1))-1;~i;i--){
for(int j=0;j<nei[x].size();j++){
int y=nei[x][j];
if(y==fa[x][0])continue;
if(!(i>>id[x][y]&1))dp[x][i]+=dp[y][0];
}
for(int j=0;j<vv[x].size();j++){
int y=vv[x][j];
if(!(msk[y]&i))dp[x][i]=max(dp[x][i],add[y]+dp[x][i|msk[y]]);
}
}
}
int main(){dep[1]=1;
cin>>n>>m;
int now=0;
for(int i=1;i<=m;i++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
if(!z)nei[x].pb(y),nei[y].pb(x);
else cx[++now]=x,cy[now]=y,cv[now]=z;
}
m=now;
dfs();
int ans=0;
for(int i=1;i<=m;i++)ca[i]=lca(cx[i],cy[i]),ans+=cv[i],dep[cx[i]]%2==dep[cy[i]]%2&&(vv[ca[i]].pb(i),0);
dfs0();
cout<<ans-dp[1][0];
return 0;
}