【51nod1325】【JZOJ4528】两棵树的问题
Description
http://www.51nod.com/Challenge/Problem.html#!#problemId=1325
Solution
枚举根,那么两棵树选择的一定是包含根的连通块。
也就是说对于一个点x如果要选择它,它在两棵树到根的路径都必须选择。
考虑网络流,每个点向它在两棵树上的父亲连边,流量为inf,源点向所有正权点连流量为权的边,每个负权点向汇点连流量为权的绝对值的边,正权点和减去最小割就是答案。
这个模型的意义:如果要保留一条正权边,那么它的后继的负权都要被割掉,所以它是对的。(其实就是最大权闭合子图)
Code
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#define fo(i,j,k) for(int i=j;i<=k;++i)
#define fd(i,j,k) for(int i=j;i>=k;--i)
#define rep(i,x) for(int i=ls[x];i;i=nx[i])
using namespace std;
const int N=330,M=2200;
int to[M],nx[M],ls[N],vl[M],num=1,inf=1e9;
void link(int u,int v,int w){
to[++num]=v,nx[num]=ls[u],ls[u]=num,vl[num]=w;
to[++num]=u,nx[num]=ls[v],ls[v]=num,vl[num]=0;
}
void clear(){
memset(ls,0,sizeof(ls)),num=1;
}
int n;
struct edge{
int to[M],nx[M],ls[N],num;
void link(int u,int v){
to[++num]=v,nx[num]=ls[u],ls[u]=num;
}
void pre(int x,int fr){
rep(i,x){
int v=to[i];
if(v==fr) continue;
::link(v,x,inf);
pre(v,x);
}
}
void init(){
memset(ls,0,sizeof(ls)),num=0;
fo(i,2,n){
int u,v;
scanf("%d %d",&u,&v);
link(u,v),link(v,u);
}
}
}A,B;
int a[N],h[N];
int S,T;
queue<int> q;
bool bfs(){
memset(h,0,sizeof(h));
h[S]=1,q.push(S);
for(;q.size();){
int x=q.front();q.pop();
rep(i,x) if(vl[i]){
int v=to[i];
if(!h[v]) h[v]=h[x]+1,q.push(v);
}
}
return h[T]>0;
}
int flow(int x,int t){
if(x==T) return t;
int fl=t;
rep(i,x) if(vl[i]){
int v=to[i];
if(h[v]==h[x]+1){
int tmp=flow(v,min(t,vl[i]));
if(tmp){
t-=tmp,vl[i]-=tmp,vl[i^1]+=tmp;
if(!t) break;
}
}
}
if(fl==t) h[x]=-1;
return fl-t;
}
int main()
{
int Tp;
scanf("%d",&Tp);
for(;Tp--;){
scanf("%d",&n);
fo(i,1,n) scanf("%d",&a[i]);
A.init(),B.init();
S=n+1,T=S+1;
int ans=0;
fo(rt,1,n){
clear();
int tmp=0;
fo(i,1,n) if(a[i]>0) tmp+=a[i],link(S,i,a[i]);
else if(a[i]<0) link(i,T,-a[i]);
A.pre(rt,0),B.pre(rt,0);
for(;bfs();) tmp-=flow(S,inf);
ans=max(ans,tmp);
}
printf("%d\n",ans);
}
}