题意
给定一个带边权的(K_{n,n}),边权表示为({a_{i,j}})。
(q)次询问,每次给定(C):
为左部和右部点分别设置一个顶标({x_i},{y_i})。
你要格外确定一个花费(S),使得(forall i,j, ext{s.t.} x_i+y_jge a_{i,j}-frac{S}{C}),最小化(sum x_i+sum y_i+S)
顶标与花费均为非负实数。
(Nle 500,a_{i,j}in[0,10^9],Cin (0,10^9])
做法
写成线性规划的形式:
[egin{aligned}
& ext{minimize} sum x_i+sum y_i+S\
& ext{s.t.}egin{cases}
x_i+y_j+frac{S}{C}ge a_{i,j}&(1le i,jle n)\
x_ige 0,y_ige 0,Sge 0
end{cases}
end{aligned}]
令(z=frac{S}{C})
[egin{aligned}
& ext{minimize} sum x_i+sum y_i+Ccdot z\
& ext{s.t.}egin{cases}
x_i+y_j+zge a_{i,j}&(1le i,jle n)\
x_ige 0,y_ige 0,zge 0
end{cases}
end{aligned}]
写成对偶的形式:
[egin{aligned}
& ext{maximize} sumlimits_{i=1}^n sumlimits_{j=1}^n a_{i,j}x_{i,j}\
& ext{s.t.}egin{cases}
sumlimits_{j=1}^n x_{i,j}le 1&(1le ile n)\
sumlimits_{i=1}^n x_{i,j}le 1&(1le jle n)\
sumlimits_{i=1}^n x_{i,j}le C\
x_{i,j}ge 0\
end{cases}
end{aligned}]
可以看作是二分图匹配:将(x_{i,j})值域定义为({0,1}),感性理解挺对的。
然后就是限制匹配数为(C)的最大权二分图匹配。
可以用KM实现
code
放份板子
#include<bits/stdc++.h>
typedef long long LL;
typedef double dl;
#define opt operator
#define pb push_back
#define pii std::pair<LL,LL>
const LL maxn=509,mod=998244353,inf=1e9;
LL Read(){
LL x(0),f(1); char c=getchar();
while(c<'0' || c>'9'){
if(c=='-') f=-1; c=getchar();
}
while(c>='0' && c<='9'){
x=(x<<3ll)+(x<<1ll)+c-'0'; c=getchar();
}return x*f;
}
void Chkmin(LL &x,LL y){
if(y<x) x=y;
}
void Chkmax(LL &x,LL y){
if(y>x) x=y;
}
LL add(LL x,LL y){
return x+=y,x>=mod?x-mod:x;
}
LL dec(LL x,LL y){
return x-=y,x<0?x+mod:x;
}
LL mul(LL x,LL y){
return 1ll*x*y%mod;
}
LL Pow(LL base,LL b){
LL ret(1); while(b){
if(b&1) ret=mul(ret,base); base=mul(base,base); b>>=1;
}return ret;
}
LL n,q;
LL pre[maxn],a[maxn][maxn],ans[maxn],mtx[maxn],mty[maxn],vall[maxn],valr[maxn];
void Revese(LL y){
while(y){
LL x(pre[y]); LL tmp(mty[x]);
mty[x]=y; mtx[y]=x;
y=tmp;
}
}
void Bfs(){
static LL markx[maxn],marky[maxn],slack[maxn];
for(LL i=1;i<=n;++i){
markx[i]=marky[i]=pre[i]=0;
slack[i]=inf;
}
std::queue<LL> que;
for(LL i=1;i<=n;++i) if(!mty[i]){
que.push(i);
}
while(true){
while(que.size()){
LL u(que.front()); que.pop();
markx[u]=1;
for(LL i=1;i<=n;++i) if(!marky[i]){
LL gap(vall[u]+valr[i]-a[u][i]);
if(gap<slack[i]){
pre[i]=u;
slack[i]=gap;
if(!gap){
if(!mtx[i]){
Revese(i); return;
}else{
marky[i]=1;
que.push(mtx[i]);
}
}
}
}
}
LL d(inf),pos(0);
for(LL i=1;i<=n;++i) if(!marky[i]){
if(slack[i]<d){
pos=i; d=slack[i];
}
}
for(LL i=1;i<=n;++i){
if(markx[i]) vall[i]-=d;
if(marky[i]) valr[i]+=d;
else slack[i]-=d;
}
if(!mtx[pos]){
Revese(pos); return;
}
marky[pos]=1;
que.push(mtx[pos]);
}
}
void Fir(){
for(LL i=1;i<=n;++i){
vall[i]=1e9;
}
for(LL i=1;i<=n;++i){
valr[i]=0;
}
for(LL i=1;i<=n;++i){
Bfs();
for(LL j=1;j<=n;++j){
if(mty[j]){
ans[i]+=a[j][mty[j]];
}
}
}
}
int main(){
LL tmp(Read());
n=Read(); q=Read();
for(LL i=1;i<=n;++i){
for(LL j=1;j<=n;++j){
a[i][j]=Read();
}
}
Fir();
while(q--){
LL C(Read());
printf("%lld.0
",ans[std::min(C,n)]);
}
return 0;
}