标签:min 直线 fill 最大 ++ with time print math
Yuta has a graph \(G\) with \(n\) nodes \((i,j)(1 \leq i \leq n,1 \leq j \leq m)\). There is an edge between \((a,b)\) and \((c,d)\) if and only if \(|a-c|+|b-d|=1\). Each edge has its weight.
Now Yuta wants to calculate the minimum weight \(K\)-matching of \(G\).
\(1 \leq n \leq 4 \times 10^4,1 \leq m \leq 4\)
https://blog.csdn.net/The_star_is_at/article/details/76919415
应该是一个经典的 Idea 了。令 \(w_i\) 为匹配数为 \(i\) 时的最小匹配,那么因为匹配是一个费用流问题,所以一定有 \(w_{i+1}-w_{i} \geq w_{i}-w_{i-1}\)。
考虑把 \(w_i\) 看成平面上的点 \((i,w_i)\),那么显然所有点都分布在一个下凸壳上,因此肯定存在一个斜率 \(d\),使得这个斜率的直线与下凸壳的切点恰好为 \((K,w_K)\),即 \((K,w_K)\) 为 \(w_i-id\) 最小的点,这相当于把边权减去 \(d\) 后的最小匹配。
因此可以二分斜率 \(d\),然后求出边权全部减去 \(d\) 后的最小匹配的值以及最小匹配中有多少条边,根据最小匹配中的边数和 \(K\) 的大小关系来决定二分的方向。这样问题就转化成了求 \(O(\log n)\) 次网格图最大匹配。这是一个轮廓线 DP 的经典问题,可以在 \(O(nm2^m)\) 内解决。
因此总的时间复杂度为 \(O(nm2^m\log n)\)。
要注意的是二分上界不能设为 \(10^9\) 级别,考虑一条 \(1\) 和 \(10^9\) 交错的链,那么在最后一次增广的时候所有的 \(1\) 都会变成 \(10^9\),因此二分上界应该是 \(10^9 \times \frac{nm}{2}\),标程把上界设为了 \(10^{14}\),DP 时刚好不会爆 long long
。
CO int64 inf=1e18;
int n,m,A[40001][4],B[40001][4];
int64 f[2][5][1<<4];int g[2][5][1<<4];
pair<int64,int> solve(int64 mid){
for(int i=0;i<=m;++i) fill(f[0][i],f[0][i]+(1<<m),inf);
f[0][m][(1<<m)-1]=g[0][m][(1<<m)-1]=0;
int o=1;
for(int u=1;u<=n;++u){
for(int i=0;i<=m;++i) fill(f[o][i],f[o][i]+(1<<m),inf);
copy(f[o^1][m],f[o^1][m]+(1<<m),f[o][0]);
copy(g[o^1][m],g[o^1][m]+(1<<m),g[o][0]);
for(int i=0;i<m;++i)for(int s=0;s<1<<m;++s){
if(i<m-1){
int t=s|1<<i|1<<(i+1);
int64 v=f[o][i][s]+B[u][i]-mid;
int c=g[o][i][s]+1;
if(f[o][i+2][t]>v or (f[o][i+2][t]==v and g[o][i+2][t]<c))
f[o][i+2][t]=v,g[o][i+2][t]=c;
}
if(u>1 and ~s>>i&1){
int t=s|1<<i;
int64 v=f[o][i][s]+A[u-1][i]-mid;
int c=g[o][i][s]+1;
if(f[o][i+1][t]>v or (f[o][i+1][t]==v and g[o][i+1][t]<c))
f[o][i+1][t]=v,g[o][i+1][t]=c;
}
int t=s&~(1<<i);
int64 v=f[o][i][s];
int c=g[o][i][s];
if(f[o][i+1][t]>v or (f[o][i+1][t]==v and g[o][i+1][t]<c))
f[o][i+1][t]=v,g[o][i+1][t]=c;
}
o^=1;
}
int64 ans=inf;int K=0;
for(int s=0;s<1<<m;++s)
if(ans>f[o^1][m][s] or (ans==f[o^1][m][s] and K<g[o^1][m][s]))
ans=f[o^1][m][s],K=g[o^1][m][s];
return make_pair(ans,K);
}
void real_main(){
read(n),read(m);
int K=read<int>();
for(int i=1;i<n;++i)for(int j=0;j<m;++j) read(A[i][j]);
for(int i=1;i<=n;++i)for(int j=0;j<m-1;++j) read(B[i][j]);
int64 l=0,r=1e14;
while(l<r){
int64 mid=(l+r)>>1;
if(solve(mid).second>=K) r=mid;
else l=mid+1;
}
printf("%lld\n",solve(l).first+l*K);
}
int main(){
for(int T=read<int>();T--;) real_main();
return 0;
}
标签:min 直线 fill 最大 ++ with time print math
原文地址:https://www.cnblogs.com/autoint/p/12302969.html