【BZOJ2756】奇怪的游戏
这道题我自己想出来了一半,是二分答案检验部分。
黑白染色。
分$n \times m$的奇偶性讨论。
当$n \times m$为奇数时,黑格子和白格子不一样多,平均值和需要的次数直接可以算出来。
当$n \times m$为偶数时,黑格子和白格子一样多,当且仅当两者的权值和相等时才有解(若不等,把他们都$+1$,还是不等),二分答案+检验即可。
一开始写的$int$,全炸了,换成$LL$就可以了。
#include<cstdio> #include<cstring> #include<iostream> #include<vector> #include<queue> #include<cmath> #define ri register int #define N 50000 #define INF 10000000000007LL #define S 0 #define T (n*m+1) #define LL long long using namespace std; int n,m; int a[100][100]; struct graph { vector<int> to,ed[N]; vector<LL> w; int cur[N],d[N]; void clear() { for (ri i=S;i<=T;i++) ed[i].clear(); to.clear(); w.clear(); } void add_edge(int u,int v,LL w1) { to.push_back(v); w.push_back(w1); ed[u].push_back(to.size()-1); to.push_back(u); w.push_back(0); ed[v].push_back(to.size()-1); } bool bfs() { queue<int> q; memset(d,0x3f,sizeof(d)); d[0]=0; q.push(0); while (!q.empty()) { int x=q.front(); q.pop(); for (ri i=0,l=ed[x].size();i<l;i++) { int e=ed[x][i]; if (w[e] && d[x]+1<d[to[e]]) { d[to[e]]=d[x]+1; q.push(to[e]); } } } return d[T]<1000000007; } LL dfs(int x,LL limit) { //cout<<x<<" "<<limit<<" "<<endl; if (x==T || !limit) return limit; LL tot=0; for (ri &i=cur[x];i<ed[x].size();i++) { int e=ed[x][i]; if (d[to[e]]==d[x]+1 && w[e]) { LL f=dfs(to[e],min(limit,w[e])); if (!f) continue; w[e]-=f; w[1^e]+=f; tot+=f; limit-=f; if (!limit) return tot; } } return tot; } LL dinic() { LL ret=0; while (bfs()) { memset(cur,0,sizeof(cur)); ret+=dfs(S,INF); } return ret; } } G; bool can(LL x) { G.clear(); LL sum=0; for (ri i=1;i<=n;i++) for (ri j=1;j<=m;j++) if (a[i][j]>x) return 0; for (ri i=1;i<=n;i++) { for (ri j=1;j<=m;j++) { if ((i+j)%2==1) { sum+=x-a[i][j]; G.add_edge(S,(i-1)*m+j,x-a[i][j]); if (i<n) G.add_edge((i-1)*m+j,i*m+j,INF); if (i>1) G.add_edge((i-1)*m+j,(i-2)*m+j,INF); if (j<m) G.add_edge((i-1)*m+j,(i-1)*m+j+1,INF); if (j>1) G.add_edge((i-1)*m+j,(i-1)*m+j-1,INF); } else { G.add_edge((i-1)*m+j,T,x-a[i][j]); } } } if (G.dinic()==sum) return 1; else return 0; } LL search() { LL lb=-1,rb=INF; for (ri i=1;i<=n;i++) for (ri j=1;j<=m;j++) if (a[i][j]>lb) lb=a[i][j]; LL ans=-1; while (lb<=rb) { LL mid=(lb+rb)/2; //cout<<mid<<endl; if (can(mid)) ans=mid,rb=mid-1; else lb=mid+1; } return ans; } LL solve() { int nb=0,nw=0; LL sb=0,sw=0; scanf("%d %d",&n,&m); for (ri i=1;i<=n;i++) { for (ri j=1;j<=m;j++) { scanf("%d",&a[i][j]); if ((i+j)%2==1) { nb++; sb+=a[i][j]; } else { nw++; sw+=a[i][j]; } } } if (nb==nw) { if (sw!=sb) return -1; else return (LL)((search()*1LL*n*1LL*m-sb-sw)/2); } else { double x=(sw*nb-sb*nw)/(double)(nw-nb); if (fabs(x-(LL)x)<1e-5) { double av=(sb+sw+2LL*(LL)x)/(double)(nw+nb); if (fabs(av-(LL)av)<1e-5) return can((LL)av)?(LL)x:-1; else return -1; } else return -1; } } int main() { int tt; scanf("%d",&tt); while (tt--) printf("%lld\n",solve()); }