题目
给你一棵大小为
n
n
n的树,还有
m
m
m条路径。
每条路径都可以染成红色或蓝色,各有一定的贡献。
对于每一条边,每条经过它的红色路径会带来一些贡献,每条经过它的蓝色路径也会带来一些贡献。
而且经过这条边的红色路径和蓝色路径的条数都有上限。
问满足所有限制条件之后的最小贡献是多少。
思考历程
打了前两档暴力,并且由于没有输出-1
,所以第一档暴力还挂了。
第三档暴力想出了暴力dp的做法,但没有时间打。
正解
显然是个线性规划嘛,只是想不想得到的问题。
先说pty的做法:
先假设所有的路径都选红色,接下来要将一些路径改选成蓝色。
那么每条边上,经过它的路径中改选成蓝色的路径条数在一定区间内(记为
[
L
u
,
R
u
]
[L_u,R_u]
[Lu,Ru])
先计算出每条路径改选之后贡献。
设
x
i
x_i
xi为第
i
i
i条路径选或不选的状态。
考虑对一条边
(
u
,
f
a
)
(u,fa)
(u,fa)的限制:
∑
i
为
经
过
(
u
,
f
a
)
的
路
径
x
i
∈
[
L
u
,
R
u
]
\sum_{i为经过(u,fa)的路径}{x_i}\in [L_u,R_u]
∑i为经过(u,fa)的路径xi∈[Lu,Ru]
可以转化成这样的限制
∑
x
i
+
a
u
=
R
u
\sum{x_i}+a_u=R_u
∑xi+au=Ru,
∑
x
i
−
b
u
=
L
u
\sum{x_i}-b_u=L_u
∑xi−bu=Lu
两式相减,得
a
u
+
b
u
=
R
u
−
L
u
a_u+b_u=R_u-L_u
au+bu=Ru−Lu,相当于
a
u
≤
R
u
−
L
u
a_u\leq R_u-L_u
au≤Ru−Lu
于是这就成了个下界为
0
0
0的差分约束模型:
x
i
≤
1
x_i\leq 1
xi≤1并且
a
u
≤
R
u
−
L
u
a_u\leq R_u-L_u
au≤Ru−Lu,要满足
∑
x
i
+
a
u
=
R
u
\sum x_i+a_u=R_u
∑xi+au=Ru,求最小贡献。
考虑对于一棵树上,每个点都有个方程,将这个方程跟所有儿子的方程作差,会得到什么。
∑
i
为
u
出
发
的
路
径
x
i
−
∑
i
为
u
结
束
的
路
径
x
i
+
a
u
−
∑
a
s
o
n
=
R
u
−
∑
R
s
o
n
\sum_{i为u出发的路径}x_i-\sum_{i为u结束的路径}x_i+a_u-\sum a_{son}=R_u-\sum R_{son}
∑i为u出发的路径xi−∑i为u结束的路径xi+au−∑ason=Ru−∑Rson
考虑费用流。假如式子左边是
F
出
−
F
入
F_出-F_入
F出−F入
对于每个路径
(
x
,
y
)
(x,y)
(x,y),
y
y
y向
x
x
x连边
(
1
,
V
)
(1,V)
(1,V)(
V
V
V表示贡献)。
y
y
y的流出加一,
x
x
x的流入加一。
对于每条边
(
u
,
f
a
)
(u,fa)
(u,fa),
u
u
u向
f
a
fa
fa连边
(
R
u
−
L
u
,
0
)
(R_u-L_u,0)
(Ru−Lu,0)。分析类似。
这样连边之后,
F
出
−
F
入
F_出-F_入
F出−F入不一定会为
0
0
0。为了保证流量平衡,对于
R
u
−
∑
R
s
o
n
R_u-\sum R_{son}
Ru−∑Rson为v正数的点
u
u
u,连
(
S
,
u
)
(S,u)
(S,u)补够流入;反之同理。
接下来跑最小费用最大流即可。有解的条件是:和源点汇点连的边都满流。
(我终于发现了原来zkw费用流跑带负权的边不靠谱,于是打了dinic。)
然后说题解的做法:
同样也要进行一开始的处理。
对于每条边
(
u
,
f
a
)
(u,fa)
(u,fa),连一条上下界为
[
L
u
,
R
u
]
[L_u,R_u]
[Lu,Ru]的费用为
0
0
0的边。
对于每条路径
(
x
,
y
)
(x,y)
(x,y),从
S
S
S向
y
y
y连上下界为
[
1
,
1
]
[1,1]
[1,1]的费用为
0
0
0的边。
从
x
x
x向
T
T
T连一条上下界为
[
1
,
1
]
[1,1]
[1,1]的费用为
0
0
0的边。
从
y
y
y向
x
x
x连一条上下界为
[
0
,
1
]
[0,1]
[0,1]的费用为
V
V
V的边。(
V
V
V为红改蓝的贡献)
跑上下界费用流。
这样给
y
y
y的
f
出
−
f
入
f_出-f_入
f出−f入减一,给
x
x
x的
f
出
−
f
入
f_出-f_入
f出−f入加一。
要使流量平衡,要么走
(
y
,
x
)
(y,x)
(y,x),意味着红变蓝;要么走树上的路径,保留原样。
代码
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <climits>
#include <cassert>
#define N 160
int n,m;
struct EDGE{
int to;
EDGE *las;
} e[N];
int ne;
EDGE *last[N];
int fa[N],A[N],B[N],eA[N],eB[N];
int sumA[N],sumB[N];
int bz[N],tmp[N];
struct Path{
int u,v,pA,pB;
} p[N];
struct EDGE2{
int to,c,w;
EDGE2 *las;
} e2[N*3*2];
int ne2;
EDGE2 *last2[N];
int S,T;
#define rev(ei) (e2+(int((ei)-e2)^1))
inline void link(int u,int v,int c,int w){
e2[ne2]={v,c,w,last2[u]};
last2[u]=e2+ne2++;
e2[ne2]={u,0,-w,last2[v]};
last2[v]=e2+ne2++;
}
bool vis[N];
int dis[N];
EDGE2 *cur[N];
int maxflow,mincost;
int dfs(int x,int s){
if (x==T){
maxflow+=s;
mincost+=dis[T]*s;
return s;
}
vis[x]=1;
int have=s;
for (EDGE2 *ei=last2[x];ei;ei=ei->las)
if (ei->c && !vis[ei->to] && dis[x]+ei->w==dis[ei->to]){
int t=dfs(ei->to,min(have,ei->c));
ei->c-=t,rev(ei)->c+=t,have-=t;
if (!have)
return s;
}
return s-have;
}
int cop[N];
int q[N*N];
bool inq[N];
inline bool change(){
memcpy(cop,dis,sizeof dis);
memset(dis,127,sizeof dis);
dis[S]=0;
inq[S]=1;
int head=0,tail=0;
q[0]=S;
while (head<=tail){
int x=q[head++];
for (EDGE2 *ei=last2[x];ei;ei=ei->las)
if (ei->c && dis[x]+ei->w<dis[ei->to]){
dis[ei->to]=dis[x]+ei->w;
if (!inq[ei->to]){
q[++tail]=ei->to;
inq[ei->to]=1;
}
}
inq[x]=0;
}
for (int i=1;i<=T;++i)
if (dis[i]!=cop[i])
return 1;
return 0;
}
inline void flow(){
mincost=maxflow=0;
while (change())
do
memset(vis,0,sizeof vis);
while (dfs(S,INT_MAX));
}
void init(int x){
for (EDGE *ei=last[x];ei;ei=ei->las){
sumA[ei->to]=sumA[x]+eA[ei->to];
sumB[ei->to]=sumB[x]+eB[ei->to];
init(ei->to);
}
}
void init2(int x){
int s=B[x];
for (EDGE *ei=last[x];ei;ei=ei->las){
init2(ei->to);
A[ei->to]=bz[ei->to]-A[ei->to];
link(ei->to,x,B[ei->to]-A[ei->to],0);
bz[x]+=bz[ei->to];
s-=B[ei->to];
}
if (s>0)
link(S,x,s,0);
else if (s<0)
link(x,T,-s,0);
}
int main(){
freopen("C.in","r",stdin);
freopen("C.out","w",stdout);
scanf("%d%d",&n,&m);
bool _2=1;
for (int i=2;i<=n;++i){
scanf("%d%d%d%d%d",&fa[i],&A[i],&B[i],&eA[i],&eB[i]);
A[i]=min(A[i],m);
B[i]=min(B[i],m);
e[ne]={i,last[fa[i]]};
last[fa[i]]=e+ne++;
}
S=n+1,T=n+2;
init(1);
for (int i=2;i<=n;++i)
eB[i]-=eA[i];
int sum=0;
for (int i=1;i<=m;++i){
scanf("%d%d%d%d",&p[i].u,&p[i].v,&p[i].pA,&p[i].pB);
int u=p[i].u,v=p[i].v;
bz[v]++,bz[u]--;
p[i].pA+=sumA[v]-sumA[u];
p[i].pB+=sumB[v]-sumB[u];
p[i].pB-=p[i].pA;
sum+=p[i].pA;
link(p[i].v,p[i].u,1,p[i].pB);
}
init2(1);
for (int i=1;i<=n;++i)
if (A[i]>B[i]){
printf("-1\n");
return 0;
}
flow();
int tmp=0;
for (EDGE2 *ei=last2[S];ei;ei=ei->las)
if (ei->c){
printf("-1\n");
return 0;
}
for (EDGE2 *ei=last2[T];ei;ei=ei->las)
if (rev(ei)->c){
printf("-1\n");
return 0;
}
printf("%d\n",mincost+sum);
return 0;
}
总结
线性规划真是个大坑……