题目大意
给定n个点m条边的无向图,你需要删掉一些边,使得此图没有长度为偶数的简单环。
删掉第i条边有Ci的花费,有些边又是不能删的。不能删的边形成图的一棵生成树。
n≤1000
m≤5000
点的度数不超过10
分析
首先一条非树边的两个端点在树上的距离为奇数就必须删掉。
考虑剩下的边,如果两条边覆盖的树的路径有公共边,那么也是不能同时存在的。
那么问题变成:保留一些非树边,使得图是一个仙人掌。求最少花费。
任找一点为根。设f[i]表示以i为根的子树的答案(不考虑子树内的点向子树外连的边),g[i][j]表示以i为根的子树不考虑i的儿子j为根的子树的答案。
考虑一条两端点lca为i的非树边,它必然经过i,而且会经过两条与i之间相连的树边。
由于一个点的度数不超过10,可以考虑状压DP。设h[S]表示集合S的与i相连的树边(不考虑连向父亲的边)已经被覆盖。然后一条lca为i的路径(u,v)对答案的贡献为f[u]+f[v]+C+对于路径上除去u,v,i的所有点,除去路径经过的子树后的答案(这个用g数组求)。
注意转移和答案的合并。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
const int N=1e3+5,M=5e3+5,T=1024,INF=1e9;
typedef long long LL;
int n,m,sum,f[N],G[N][N],g[T],h[N],tot,e[M],nxt[M],E[M][3],s[N],Id[N],fa[N],a[N],dep[N],st[N];
vector <int> H[N];
bool vis[N];
char c;
int read()
{
int x=0,sig=1;
for (c=getchar();c<'0' || c>'9';c=getchar()) if (c=='-') sig=-1;
for (;c>='0' && c<='9';c=getchar()) x=x*10+c-48;
return x*sig;
}
void add(int x,int y)
{
e[++tot]=y; nxt[tot]=h[x]; h[x]=tot;
}
void Init(int x)
{
for (int i=h[x];i;i=nxt[i]) if (e[i]!=fa[x])
{
fa[e[i]]=x; dep[e[i]]=dep[x]+1; Init(e[i]);
}
}
void dfs(int x)
{
for (int i=h[x];i;i=nxt[i]) if (e[i]!=fa[x])
{
s[e[i]]=s[x]-f[x]+G[x][Id[e[i]]]+f[e[i]]; st[e[i]]=st[x];
dfs(e[i]);
}
}
void dp(int x)
{
int cnt=0,i,j,k,t,d1,d2;
for (i=h[x];i;i=nxt[i]) if (e[i]!=fa[x])
{
Id[e[i]]=cnt++;
dp(e[i]);
s[e[i]]=f[e[i]]; st[e[i]]=cnt-1; dfs(e[i]);
}
for (i=h[x];i;i=nxt[i]) if (e[i]!=fa[x]) a[Id[e[i]]]=e[i];
if (!cnt) return;
t=1<<cnt;
for (i=1;i<t;i++) g[i]=-INF; g[0]=0;
vector <int> ::iterator it;
for (it=H[x].begin();it!=H[x].end();it++)
{
j=*it;
if (x!=E[j][0]) d1=1<<st[E[j][0]];else d1=0;
if (x!=E[j][1]) d2=1<<st[E[j][1]];else d2=0;
for (i=k=(t-1)^(d1|d2);i;i=(i-1)&k)
g[i|d1|d2]=max(g[i|d1|d2],g[i]+s[E[j][0]]+s[E[j][1]]+E[j][2]);
g[d1|d2]=max(g[d1|d2],s[E[j][0]]+s[E[j][1]]+E[j][2]);
}
for (i=0;i<t;i++)
{
for (j=0;j<cnt;j++) g[i]+=(((1<<j)&i)==0)*f[a[j]];
for (j=0;j<cnt;j++) if (((1<<j)&i)==0) G[x][j]=max(G[x][j],g[i]-f[a[j]]);
f[x]=max(f[x],g[i]);
}
}
int main()
{
n=read();
for (int i=read();i--;)
{
int x=read(),y=read(),w=read();
if (!w)
{
add(x,y); add(y,x); continue;
}
if (x==y) continue;
sum+=w;
E[m][0]=x; E[m][1]=y; E[m++][2]=w;
}
Init(1);
for (int i=0,j;i<m;i++)
{
for (j=1;j<=n;j++) vis[j]=0;
for (j=E[i][0];j;j=fa[j]) vis[j]=1;
for (j=E[i][1];!vis[j];j=fa[j]);
if (!((dep[E[i][0]]+dep[E[i][1]]-2*dep[j])&1)) H[j].push_back(i);
}
dp(1);
printf("%d\n",sum-f[1]);
return 0;
}