Run
题意:
给出一张图(点数为n,边数为m)的一个生成树和一些非树边。
每条非树边有一个代价w,表示炸毁这条边需要付出的代价。
求炸毁一些非树边使得图中不存在偶环所付出的最小代价。
特别地,生成树上点的度数不超过10.
数据范围:n<=1000,m<=4000
分析:
首先如果一条非树边跨越了树上奇数条边,则这条边必须炸毁。
剩下的边,如果任意两条边所跨越的树上路径有公共边,则会存在偶环。
考虑到每个节点的儿子最多只有10个,我们可以考虑状压dp.
令f[x][s]表示以x为根的子树,只考虑集合s中儿子时能保留的边的最大权值。
令g[x][i]表示以x为根的子树,不考虑x的第i个儿子时能保留的边的最大权值。
每条非树边,只在它的端点的lca处考虑影响。
为了让路径不重复,则该非树边所覆盖的路径都不能被选。
暴力从一个端点到lca统计不考虑路径上的边时的最大权值。
再与该非树边没有覆盖的部分一起更新答案。
复杂度O(n*210+n*m)
#include <vector> #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define g() getchar() template<class Q>void Scan(Q&x){ char c;int f=1; while(c=g(),c<48||c>57)if(c=='-')f=-1; for(x=0;c>47&&c<58;c=g())x=10*x+c-48; x*=f; } #define Max(x,y) if(x<y)x=y #define Min(x,y) if(x>y)x=y #define pb push_back #define rep(i,a,b) for(int i=a;i<b;++i) const int MAXN=1005; const int MAXS=1<<10; int n,m; struct edge{ int t,n; edge(int t=0,int n=0):t(t),n(n){} }e[MAXN<<1]; int head[MAXN],tot; inline void AddEdge(int s,int t){ e[++tot]=edge(t,head[s]),head[s]=tot; e[++tot]=edge(s,head[t]),head[t]=tot; } int p[MAXN],dep[MAXN]; vector<int>ch[MAXN],qy[MAXN]; void dfs(int x){ for(int i=head[x],y;i;i=e[i].n) if((y=e[i].t)!=p[x]){ p[y]=x; dep[y]=dep[x]+1; ch[x].pb(y); dfs(y); } } inline int lca(int x,int y){ if(dep[x]>dep[y])swap(x,y); while(dep[y]>dep[x])y=p[y]; while(p[x]!=p[y])x=p[x],y=p[y]; return x==y?x:p[x]; } struct edge1{ int u,v,w; inline void scan(){ Scan(u),Scan(v),Scan(w); } }tmp[MAXN*5]; int ans; inline void Init(){ Scan(n),Scan(m); rep(i,1,m+1){ tmp[i].scan(); ans+=tmp[i].w; if(!tmp[i].w){ AddEdge(tmp[i].u,tmp[i].v); } } dfs(1); rep(i,1,m+1){ if(!tmp[i].w)continue; int u=tmp[i].u,v=tmp[i].v; int Lca=lca(u,v); if((dep[u]+dep[v]-dep[Lca]*2)&1)continue; qy[Lca].pb(i); } } int dp[MAXN]; int f[MAXN][MAXS],g[MAXN][10]; inline int swim(int x,int par,int&res){ int ban=-1; while(x!=par){ if(~ban)res+=g[x][ban]; else res+=dp[x]; int s=ch[p[x]].size(); rep(i,0,s) if(x==ch[p[x]][i]){ ban=i; break; } x=p[x]; } return ban; } void tree_dp(int x){ for(int i=head[x],y;i;i=e[i].n) if((y=e[i].t)!=p[x])tree_dp(y); int s=ch[x].size(); int maxs=1<<s; rep(set,0,maxs){ int res=0; rep(i,0,s) if(set>>i&1){ res+=dp[ch[x][i]]; } Max(f[x][set],res); } int c=qy[x].size(); rep(i,0,c){ int u=tmp[qy[x][i]].u; int v=tmp[qy[x][i]].v; int res=tmp[qy[x][i]].w; int bu=max(0,1<<swim(u,x,res)); int bv=max(0,1<<swim(v,x,res)); rep(set,0,maxs){ if(bu&&(set&bu)==bu||bv&&(set&bv)==bv) continue; int ns=set^bu^bv; Max(f[x][ns],res+f[x][set]); } } rep(set,0,maxs){ rep(i,0,s) if(!(set>>i&1)){ Max(g[x][i],f[x][set]); } Max(dp[x],f[x][set]); } } inline void Solve(){ tree_dp(1); ans-=dp[1]; printf("%d\n",ans); } int main(){ freopen("run.in","r",stdin); freopen("run.out","w",stdout); Init(); Solve(); return 0; }