P15903 [TOPC 2025] Move Stone
原题传送门
前置芝士
网络流,最大流。
解法
最开始看成了只能向相邻的点转移,然后打了一个费用流过不去样例,看来没救了。
首先根据直觉,这道题应该是费用流。
常识告诉我们,每个源点应该向有值的点连流量为 \(a_{i,j}-1\),费用为 \(0\) 的边,每个值为 \(0\) 的点可以向汇点连流量为 \(1\),费用为 \(0\) 的边,题意可得每个棋子可以向与他同一列或同一行转移,那么根据经验,我们可以把每一行和每一列都新建一个点,然后我们把这一行或每一列多余的点数都转到这个多余的点上,费用为 \(0\),流量为 \(a_{i,j}-1\),然后通过这个点向与它同一列或同一行的 \(a_{i,j}=0\) 点连一条流量为 \(1\),费用为 \(1\) 的边,这时候就解决了每一行或列的转移处理。
然后我们来考虑不在同一行或同一列的转移,一个显然的做法是通过两次操作从其他值大于 \(1\) 的点来转移,那么总的操作数就会加上还为 \(0\) 的点数乘 \(2\)。
这时我们回去看费用流,我们会发现每个源点到汇点的费用始终为 \(1\),所以可以直接去掉费用流,直接用最大流,且此时贡献为 \(1\) 的点数和总费用是相等的,那么整合一下式子就可以发现答案就是值为 \(0\) 的点数乘二减去最大流。
#include<bits/stdc++.h>
#define inf 1e9
#define mod 1000000007
using namespace std;
const int maxn = 1010;
#ifdef __linux__
#define gc getchar_unlocked
#define pc putchar_unlocked
#else
#define gc _getchar_nolock
#define pc _putchar_nolock
#endif
inline bool blank(const char x) {return !(x^32)||!(x^10)||!(x^13)||!(x^9);}
template<typename Tp> inline void read(Tp &x) {x=0; register bool z=true; register char a=gc(); for(;!isdigit(a);a=gc()) if(a=='-') z=false; for(;isdigit(a);a=gc()) x=(x<<1)+(x<<3)+(a^48); x=(z?x:~x+1);}
inline void read(double &x) {x=0.0; register bool z=true; register double y=0.1; register char a=gc(); for(;!isdigit(a);a=gc()) if(a=='-') z=false; for(;isdigit(a);a=gc()) x=x*10+(a^48); if(a!='.') return x=z?x:-x,void(); for(a=gc();isdigit(a);a=gc(),y/=10) x+=y*(a^48); x=(z?x:-x);}
inline void read(char &x) {for(x=gc();blank(x)&&(x^-1);x=gc());}
inline void read(char *x) {register char a=gc(); for(;blank(a)&&(a^-1);a=gc()); for(;!blank(a)&&(a^-1);a=gc()) *x++=a; *x=0;}
inline void read(string &x) {x=""; register char a=gc(); for(;blank(a)&&(a^-1);a=gc()); for(;!blank(a)&&(a^-1);a=gc()) x+=a;}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y) {read(x),read(y...);}
template<typename Tp> inline void write(Tp x) {if(!x) return pc(48),void(); if(x<0) pc('-'),x=~x+1; register int len=0; register char tmp[64]; for(;x;x/=10) tmp[++len]=x%10+48; while(len) pc(tmp[len--]);}
inline void write(const double x) {register int a=6; register double b=x,c=b; if(b<0) pc('-'),b=-b,c=-c; register double y=5*powl(10,-a-1); b+=y,c+=y; register int len=0; register char tmp[64]; if(b<1) pc(48); else for(;b>=1;b/=10) tmp[++len]=floor(b)-floor(b/10)*10+48; while(len) pc(tmp[len--]); pc('.'); for(c*=10;a;a--,c*=10) pc(floor(c)-floor(c/10)*10+48);}
inline void write(const pair<int,double>x) {register int a=x.first; if(a<7) {register double b=x.second,c=b; if(b<0) pc('-'),b=-b,c=-c; register double y=5*powl(10,-a-1); b+=y,c+=y; register int len=0; register char tmp[64]; if(b<1) pc(48); else for(;b>=1;b/=10) tmp[++len]=floor(b)-floor(b/10)*10+48; while(len) pc(tmp[len--]); a&&(pc('.')); for(c*=10;a;a--,c*=10) pc(floor(c)-floor(c/10)*10+48);} else cout<<fixed<<setprecision(a)<<x.second;}
inline void write(const char x) {pc(x);}
inline void write(const bool x) {pc(x?49:48);}
inline void write(char *x) {fputs(x,stdout);}
inline void write(const char *x) {fputs(x,stdout);}
inline void write(const string &x) {fputs(x.c_str(),stdout);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y) {write(x),write(y...);}
int n , a[maxn][maxn] , dis[maxn * maxn] , head[maxn * maxn] , tot = 1 , cul[maxn * maxn] , id[510][510] , cnt , s , t , x[maxn] , y[maxn];
struct edge {
int to , next , w;
}e[maxn * maxn];
inline void add(int u , int v , int w) {
e[++tot] = {v , head[u] , w};
head[u] = tot;
}
inline void addedge(int u , int v , int w) {
add(u , v , w);
add(v , u , 0);
}
queue<int> q;
inline bool bfs(int s , int t) {
memset(dis , -1 , sizeof dis);
q.push(s);
dis[s] = 0;
while(!q.empty()) {
int u = q.front();
q.pop();
for(int i = head[u]; i; i = e[i].next) {
int v = e[i].to;
if(dis[v] == -1 && e[i].w) {
dis[v] = dis[u] + 1;
q.push(v);
}
}
}
return dis[t] != -1;
}
inline int dfs(int x , int t , int sum) {
if(x == t) return sum;
int last = sum;
for(int i = cul[x]; i; i = e[i].next) {
int v = e[i].to;
cul[x] = i;
if(dis[v] == dis[x] + 1 && e[i].w) {
int w = dfs(v , t , min(e[i].w , last));
last -= w;
e[i].w -= w;
e[i ^ 1].w += w;
if(!last) break;
}
}
return sum - last;
}
inline int dinic(int s , int t) {
int res = 0;
while(bfs(s , t)) {
memcpy(cul , head , sizeof head);
res += dfs(s , t , inf);
}
return res;
}
signed main() {
read(n);
for(int i = 1; i <= n; ++i) for(int j = 1; j <= n; ++j) read(a[i][j]) , id[i][j] = ++cnt;
for(int i = 1; i <= n; ++i) x[i] = ++cnt , y[i] = ++cnt;
s = ++cnt;
t = ++cnt;
int ans = 0;
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= n; ++j) {
if(a[i][j]) addedge(s , id[i][j] , a[i][j] - 1) , addedge(id[i][j] , x[i] , a[i][j] - 1) , addedge(id[i][j] , y[j] , a[i][j] - 1);
else addedge(id[i][j] , t , 1) , addedge(x[i] , id[i][j] , 1) , addedge(y[j] , id[i][j] , 1) , ans++;
}
}
write(ans * 2 - dinic(s , t));
return !("May this journey lead me to the stars!");
}
浙公网安备 33010602011771号