1. 程式人生 > >BZOJ2756: [SCOI2012]奇怪的遊戲

BZOJ2756: [SCOI2012]奇怪的遊戲

個數 inf cpp truct num mar ems zoj 滿足

題就不再念了

Solution

首先對棋盤進行黑白染色,像這樣
技術分享圖片
然後要統計白點個數,初始白點點權和以及黑點個數與初始黑點點權和
顯然的是,最終得到的值 \(X\) 可以寫作
\[X=\dfrac{WhiteSum-BlackSum}{WhiteNum-BlackNum}\]

\(WhiteNum !=BlackNum\) 時直接判斷 \(X\) 是否可行,可行則輸出解,否則無解
\(WhiteNum==BlackNum\)\(X\) 是無意義的,也就是說算不出來
但此時可以看出來,若 \(X\) 可以滿足題意 那麽 \(X+1\) 也滿足題意(任意的白點都可以有一個與它配對的黑點一起 \(+1\)

,整個圖都這麽做,\(X+1\) 也就可以加出來)
說明了什麽?可以二分了
二分 \(X\) 可以求出

二分的 \(Check()\) 要用到網絡流
設源點為 \(S\) 匯點為 \(T\)
\(S\) 到白點連容量為 \(X-val[i][j]\) 的邊,黑點到 \(T\) 連容量為 \(X-val[i][j]\) 的邊,相鄰的白點和黑點容量為INF
(腦補一下,白點和黑點都要加夠 \(X-val[i][j]\) 次,白點和黑點之間沒有限制)
如果這個圖是滿流的,說明 \(X\) 可行,否則不可行

這題時限40s,似乎可以用來給不法分子卡評測

註意:long long還是很慢的,memset,memcpy很耗時,剛開始浪數組開太大,卡了一個40s的TLE。。。

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define MAXN 100
#define MAXE 10000
#define INF (1LL<<60)
using namespace std;

LL n,m;
LL kase;
LL num[MAXN][MAXN];
LL color[MAXN][MAXN];
struct E{
    LL next,to,val;
}edge[MAXE];
LL head[MAXE],edge_num;
LL depth[MAXE],iter[MAXE];
queue<LL> que;
LL s,t;
LL cntw,cntb,sumw,sumb;
LL u[5
]={0,1,-1,0,0},v[5]={0,0,0,1,-1}; LL FLOW; LL MAX; LL index(LL i,LL j){ return i*m+j; } bool inmap(LL x,LL y){ return x<=n && x>=1 && y<=m && y>=1; } void addedge(LL x,LL y,LL v){ edge[++edge_num].next=head[x]; edge[edge_num].to=y; edge[edge_num].val=v; head[x]=edge_num; } void INIT(){ scanf("%lld%lld",&n,&m); s=m*(n+1)+1,t=m*(n+1)+2; cntw=cntb=sumw=sumb=0; LL i,j; MAX=0; for(i=1;i<=n;i++){ for(j=1;j<=m;j++){ scanf("%lld",&num[i][j]); MAX=max(MAX,num[i][j]); if((j%2)^(i%2)) color[i][j]=1,cntb++,sumb+=num[i][j]; else color[i][j]=0,cntw++,sumw+=num[i][j]; } } } void Graph(LL x){ LL i,j; edge_num=1; memset(head,0,sizeof(head)); FLOW=0; for(i=1;i<=n;i++){ for(j=1;j<=m;j++){ if(color[i][j]==0){ FLOW+=x-num[i][j]; addedge(s,i*m+j,x-num[i][j]); addedge(i*m+j,s,0); LL k; for(k=1;k<=4;k++){ LL x,y; x=i+u[k],y=j+v[k]; if(inmap(x,y)){ addedge(index(i,j),index(x,y),INF); addedge(index(x,y),index(i,j),0); } } } else{ addedge(i*m+j,t,x-num[i][j]); addedge(t,i*m+j,0); } } } } void BFS(){ memset(depth,-1,sizeof(depth)); que.push(s); depth[s]=0; LL i; while(!que.empty()){ LL fro=que.front();que.pop(); for(i=head[fro];i;i=edge[i].next){ if(edge[i].val>0 && depth[edge[i].to]==-1){ depth[edge[i].to]=depth[fro]+1; que.push(edge[i].to); } } } } LL DFS(LL x,LL f){ if(x==t) return f; for(LL &i=iter[x];i;i=edge[i].next){ if(edge[i].val>0 && depth[edge[i].to]==depth[x]+1){ LL tmp=DFS(edge[i].to,min(f,edge[i].val)); if(tmp>0){ edge[i].val-=tmp; edge[i^1].val+=tmp; return tmp; } } } return 0; } LL Dinic(){ LL re=0; while(true){ BFS(); if(depth[t]<0) break; LL tmp; memcpy(iter,head,sizeof(head)); while(true){ tmp=DFS(s,INF); if(tmp<=0){ break; } re+=tmp; } } return re; } bool check(LL x){ return FLOW==x; } void PLAN1(){ LL x=(sumw-sumb)/(cntw-cntb); if(x<MAX){ printf("-1\n"); return; } Graph(x); LL tmp=Dinic(); if(check(tmp)) printf("%lld\n",x*cntw-sumw); else printf("-1\n"); } void PLAN2(){ LL l,r; l=MAX;r=(1LL<<50); while(l<=r){ LL mid=(l+r)>>1; Graph(mid); LL tmp=Dinic(); if(check(tmp)) r=mid-1; else l=mid+1; } printf("%lld\n",l*cntw-sumw); } void solve(){ if(cntw!=cntb){ PLAN1(); } else{ if(sumw!=sumb){ printf("-1\n"); return; } else PLAN2(); } } int main(){ freopen("bzoj.in","r",stdin); freopen("out.out","w",stdout); scanf("%lld",&kase); while(kase--){ INIT(); solve(); } return 0; }

BZOJ2756: [SCOI2012]奇怪的遊戲