题目
题意
你有 n ( n ≤ 500 ) n(n\leq 500) n(n≤500)个士兵,每个士兵需要安排一个职位(法师或战士),另给定 m m m对士兵,对于其中的一对 u i , v i u_i,v_i ui,vi,如果都选战士,战队的攻击力会提高 a i a_i ai点( 4 ∣ a i 4~|~a_i 4 ∣ ai);如果都选法师,攻击力提高 c i c_i ci点( 3 ∣ c i 3~|~c_i 3 ∣ ci);如果一个战士一个法师,攻击力提高 b i b_i bi点( b i = a i 4 + c i 3 b_i=\dfrac{a_i}{4}+\dfrac{c_i}{3} bi=4ai+3ci),问战队攻击力最多提升多少点。
分析
这类“二元分配,组合加成”的问题,一般可以用总和(即所有
a
a
a、
b
b
b、
c
c
c之和)减最小割完成,建图如下:
我们的目的是使不同割法对应不同选法的损失。
图中的
1
1
1、
2
2
2是两个有加成关系士兵,割
x
x
x表示
1
1
1选战士,割
m
m
m表示
1
1
1选法师;割
y
y
y表示
2
2
2选战士,割
n
n
n表示
2
2
2选法师。于是有这几种情况:
- 割 x x x和 y y y,则损失的战斗力是 b + c b+c b+c,所以 x + y = b + c x+y=b+c x+y=b+c;
- 割 m m m和 n n n,则损失的战斗力是 a + b a+b a+b,所以 m + n = a + b m+n=a+b m+n=a+b;
- 割 x x x和 n n n或者割 y y y和 m m m,这时由于 z z z的存在,图还是联通的,所以必须再割 z z z,所以 x + n + z = y + m + z = a + c x+n+z=y+m+z=a+c x+n+z=y+m+z=a+c。
注意你可能觉得还有其他割法,但是,由于边权必须是正数(最大流最小割不能跑负容量),所以显然割集 S S S一定比 S ′ S' S′优( S ⫋ S ′ S\subsetneqq S' S⫋S′),所以只有上述几种情况。
联立得到(不定)方程组:
{
x
+
y
=
b
+
c
m
+
n
=
a
+
b
x
+
n
+
z
=
a
+
c
y
+
m
+
z
=
a
+
c
\begin{cases} x+y=b+c\\ m+n=a+b\\ x+n+z=a+c\\ y+m+z=a+c \end{cases}
⎩⎪⎪⎪⎨⎪⎪⎪⎧x+y=b+cm+n=a+bx+n+z=a+cy+m+z=a+c
其中
a
a
a、
b
b
b、
c
c
c是常数。
找到一组解:
{
x
=
y
=
b
+
c
2
m
=
n
=
a
+
b
2
z
=
a
+
c
2
−
b
\begin{cases} x=y=\dfrac{b+c}{2}\\ m=n=\dfrac{a+b}{2}\\ z=\dfrac{a+c}{2}-b \end{cases}
⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧x=y=2b+cm=n=2a+bz=2a+c−b
于是按这个建边即可,注意先乘
2
2
2使边权为整数。
(题目中
b
b
b的奇怪的表达式应该只是让
z
>
0
z>0
z>0吧= =)
代码
#include<queue>
#include<cstdio>
#include<algorithm>
using namespace std;
int read(){
int x=0;bool f=0;char c=getchar();
while(c<'0'||c>'9') f|=c=='-',c=getchar();
while(c>='0'&&c<='9') x=x*10+(c^48),c=getchar();
return f?-x:x;
}
int N,M;
#define MAXN 50000
#define MAXM 1000000
#define INF int(1e9)
#define LL long long
struct Edge{
int v,w,Next;
}E[MAXM+5];
int Adj[MAXN+5],ecnt;
void AddEdge(int u,int v,int w){
E[ecnt].v=v,E[ecnt].w=w,E[ecnt].Next=Adj[u],Adj[u]=ecnt++;
E[ecnt].v=u,E[ecnt].w=0,E[ecnt].Next=Adj[v],Adj[v]=ecnt++;
}
int Cur[MAXN+5];
int Dist[MAXN+5];
bool bfs(int T){
for(int i=0;i<=T;i++)
Cur[i]=Adj[i],Dist[i]=-1;
queue<int> Q;
Q.push(0),Dist[0]=0;
while(!Q.empty()){
int u=Q.front();Q.pop();
for(int i=Adj[u];i!=-1;i=E[i].Next){
int v=E[i].v;
if(E[i].w&&Dist[v]==-1){
Dist[v]=Dist[u]+1;
Q.push(v);
}
}
}
return Dist[T]>=0;
}
int dfs(int u,int T,int Min){
if(!Min||u==T)
return Min;
int Rest=Min,Now;
for(int &i=Cur[u];i!=-1;i=E[i].Next){
int v=E[i].v,w=E[i].w;
if(Dist[v]==Dist[u]+1&&(Now=dfs(v,T,min(Rest,w)))){
Rest-=Now,
E[i].w-=Now,
E[i^1].w+=Now;
if(!Rest)
break;
}
}
return Min-Rest;
}
LL Dinic(int T){
LL ret=0;
while(bfs(T))
ret+=dfs(0,T,INF);
return ret;
}
int main(){
while(~scanf("%d%d",&N,&M)){
ecnt=0;
for(int i=0;i<=N+1;i++)
Adj[i]=-1;
LL Sum=0;
for(int i=1;i<=M;i++){
int u=read(),v=read(),A=read()*2,B=read()*2,C=read()*2;
Sum+=A+B+C;
AddEdge(0,u,(A+B)/2),AddEdge(0,v,(A+B)/2);
AddEdge(u,N+1,(B+C)/2),AddEdge(v,N+1,(B+C)/2);
AddEdge(u,v,(A+C)/2-B),AddEdge(v,u,(A+C)/2-B);
}
printf("%lld\n",(Sum-Dinic(N+1))/2);
}
return 0;
}