题目描述
分析
树链:一条从上到下的树上简单路径
对于另外20%:
由于di=1,那么设f[i]为做完i之后,即i的整颗子树都不能再选了的最大收益,那么每次在树链顶时,更新一下f[i],就是把整条路的其他无关的点的f统计起来。为了方便把边拆成点。
100%:
一般来说,这种玄学图论问题,想不到解法要往网络流上想想。那么这道题可能是一个最大费用流。
但是流一次要强制走一段路径,怎么办呢?考虑这样建图:原图G1的边,在新的图G2中,让深度较小的点连向较大的点,费用0,流量就是覆盖次数。然后一条树链的话,就从深度最大的点连一条边到最小的点,费用c,流量1。那么原来一次覆盖就对应了一个环,跑一个最大费用循环流就可以了。
无源汇最大费用流
在这个题设下,我们流完以后,所有点的度数为0,即每次流一个环出来。
做法:
首先让正权边全部流满。
开设一个数组du[]来记录每个节点的流量情况。
du[i]=in[i](i节点所有正权入流之和)-out[i](i节点所有正权出流之和)。
由于要保证流量平衡
将du[i]<0的连s->i,费用为0,流量为[0,-du[i]]的边。
将du[i]>0的连i->t,费用为0,流量为[0,du[i]]的边。
对于原图的u->v,cost,[0,up]
Cost>0,连v->u,cost,[0,up]
Cost<0,连u->v,-cost,[0,up]
Cost=0,照样
然后对新图做最小费用流,答案就是正边之和-第二次的费用。
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<bitset>
using namespace std;
typedef long long ll;
typedef double db;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
const int N=1000005;
struct rec
{
int x,y,z;
}lu[N];
int t1,b1[N],c1[N],e1[N],next1[N],first1[N],dep[N],du[N];
int tt,b[N],c[N],inv[N],next[N],first[N],pd[N],now[N];
int n,m,x,y,z,i,j,k,T,st,en,ttt;
ll cost[N],ans,dis[N];
void cr1(int x,int y,int z)
{
t1++;
b1[t1]=y;
c1[t1]=z;
e1[t1]=x;
next1[t1]=first1[x];
first1[x]=t1;
}
void dfs(int x,int y)
{
dep[x]=dep[y]+1;
//fa[x]=y;
for(int p=first1[x];p;p=next1[p])
if (b1[p]!=y)
dfs(b1[p],x);
}
void cr(int x,int y,int inv1,int cost1,int z)
{
tt++;
b[tt]=y;
cost[tt]=cost1;
c[tt]=z;
inv[tt]=inv1+tt;
next[tt]=first[x];
first[x]=tt;
}
int change()
{
ll mn=1e15;
int i,j,p;
fo(i,st,en)
if (pd[i]==ttt)
for(p=first[i];p;p=next[p])
if (c[p]&&pd[b[p]]!=ttt)
mn=min(mn,dis[b[p]]+cost[p]-dis[i]);
if (mn==1e15) return 0;
fo(i,st,en)
if (pd[i]==ttt)
dis[i]+=mn;
return 1;
}
int flow(int x,int y)
{
pd[x]=ttt;
if (x==en)
{
ans-=dis[st]*y;
return y;
}
for(int p=now[x];p;p=next[p])
if (pd[b[p]]!=ttt&&c[p]&&dis[b[p]]+cost[p]==dis[x])
{
int l=flow(b[p],min(y,c[p]));
if (l)
{
c[p]-=l;
c[inv[p]]+=l;
now[x]=p;
return l;
}
}
now[x]=0;
return 0;
}
void clear()
{
fo(i,1,n) first1[i]=0;
fo(i,st,en)
{
first[i]=0;
dis[i]=0;
pd[i]=0;
du[i]=0;
}
ans=t1=ttt=tt=0;
}
int main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
scanf("%d",&T);
while (T--)
{
clear();
scanf("%d %d",&n,&m);
fo(i,1,n-1)
{
scanf("%d %d %d",&x,&y,&z);
cr1(x,y,z);
cr1(y,x,z);
}
dfs(1,0);
fo(i,1,n-1)
{
x=(i-1)*2+1;
if (dep[e1[x]]>dep[b1[x]]) x++;
cr(e1[x],b1[x],1,0,c1[x]);
cr(b1[x],e1[x],-1,0,0);
}
fo(i,1,m)
{
scanf("%d %d %d",&x,&y,&z);
if (dep[x]>dep[y]) swap(x,y);
ans+=z;
du[y]--;
du[x]++;
cr(x,y,1,z,1);
cr(y,x,-1,-z,0);
}
st=0;
en=n+1;
fo(i,1,n)
if (du[i]>0)
{
cr(0,i,1,0,du[i]);
cr(i,0,-1,0,0);
}else
{
cr(i,en,1,0,-du[i]);
cr(en,i,-1,0,0);
}
do
{
fo(i,st,en) now[i]=first[i];
do ttt++;
while (flow(st,N));
}while (change());
printf("%lld\n",ans);
}
}