• bzoj2756 [SCOI2012]奇怪的游戏


    Description

    Blinker最近喜欢上一个奇怪的游戏。 

    这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数。每次 Blinker 会选择两个相邻的格子,并使这两个数都加上 1。 

    现在 Blinker 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成同一个数则输出-1。 

    Input

    输入的第一行是一个整数T,表示输入数据有T轮游戏组成。 
    每轮游戏的第一行有两个整数N和M, 分别代表棋盘的行数和列数。 
    接下来有N行,每行 M个数。 

    Output

      对于每个游戏输出最少能使游戏结束的次数,如果永远不能变成同一个数则输出-1。

    Sample Input

    2
    2 2
    1 2
    2 3
    3 3
    1 2 3
    2 3 4
    4 3 2

    Sample Output

    2
    -1

    HINT

    【数据范围】 

    对于30%的数据,保证  T<=10,1<=N,M<=8 
    对于100%的数据,保证  T<=10,1<=N,M<=40,所有数为正整数且小于1000000000 

    正解:二分+最大流。

    这道题还是很不容易想啊,不过我写了个骗分居然有$90$分。。

    首先我们肯定是要黑白染色,相邻的点染不同的颜色。

    然后我们按照格子数分奇偶讨论:

    如果格子数为偶数,那么黑点和白点个数相同,于是所有黑点的和与所有白点的和必须相同,否则一定不合法。

    因为当黑点和白点个数相同时,每个点的权值$+1$,等价于将相邻的上下两点匹配一次,所以我们发现每个点的最终权值是满足单调性的。也就是说如果$v$合法,那么$v+1$也一定合法,于是我们直接二分最终的权值,建图跑最大流就行了。

    当格子数为奇数时,黑点与白点个数不同。那么我们可以直接算出最终每个格子的权值。

    由$v*num_{black}-sum_{black}=v*num_{white}-sum_{white}$,解得$v=(sum_{black}-sum_{while})/(num_{black}-num_{white})$。

    $num$为格子数量,$sum$为初始的格子权值和。

    那么我们判断一下$v$是否合法就行了。

      1 #include <bits/stdc++.h>
      2 #define il inline
      3 #define RG register
      4 #define ll long long
      5 #define inf (1LL<<60)
      6 #define N (1000010)
      7 #define pos(i,j) ((i-1)*m+j)
      8 
      9 using namespace std;
     10 
     11 struct edge{ int nt,to; ll flow,cap; }g[N<<1];
     12 
     13 int head[N],d[N],q[N],a[42][42],S,T,n,m,num,tot;
     14 ll x,y,ans;
     15 
     16 il int gi(){
     17   RG int x=0,q=1; RG char ch=getchar();
     18   while ((ch<'0' || ch>'9') && ch!='-') ch=getchar();
     19   if (ch=='-') q=-1,ch=getchar();
     20   while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar();
     21   return q*x;
     22 }
     23 
     24 il void insert(RG int from,RG int to,RG ll cap){
     25   g[++num]=(edge){head[from],to,0,cap},head[from]=num; return;
     26 }
     27 
     28 il int bfs(RG int S,RG int T){
     29   for (RG int i=1;i<=T;++i) d[i]=0;
     30   RG int h=0,t=1; q[t]=S,d[S]=1;
     31   while (h<t){
     32     RG int x=q[++h],v;
     33     for (RG int i=head[x];i;i=g[i].nt){
     34       v=g[i].to;
     35       if (!d[v] && g[i].cap>g[i].flow){
     36     d[v]=d[x]+1,q[++t]=v;
     37     if (v==T) return 1;
     38       }
     39     }
     40   }
     41   return d[T];
     42 }
     43 
     44 il ll dfs(RG int x,RG int T,RG ll a){
     45   if (!a || x==T) return a; RG ll flow=0,f; RG int v;
     46   for (RG int i=head[x];i;i=g[i].nt){
     47     v=g[i].to;
     48     if (d[v]==d[x]+1 && g[i].cap>g[i].flow){
     49       f=dfs(v,T,min(a,g[i].cap-g[i].flow));
     50       if (!f){ d[v]=0; continue; }
     51       g[i].flow+=f,g[i^1].flow-=f;
     52       flow+=f,a-=f; if (!a) return flow;
     53     }
     54   }
     55   return flow;
     56 }
     57 
     58 il ll maxflow(RG int S,RG int T){
     59   RG ll flow=0;
     60   while (bfs(S,T)) flow+=dfs(S,T,inf);
     61   return flow;
     62 }
     63 
     64 il int check(RG ll key){
     65   for (RG int i=1;i<=T;++i) head[i]=0; num=1;
     66   for (RG int i=1;i<=n;++i)
     67     for (RG int j=1;j<=m;++j){
     68     if ((i+j)&1) insert(S,pos(i,j),key-a[i][j]),insert(pos(i,j),S,0);
     69     else insert(pos(i,j),T,key-a[i][j]),insert(T,pos(i,j),0);
     70     }
     71   for (RG int i=1;i<=n;++i)
     72     for (RG int j=1;j<=m;++j)
     73       if ((i+j)&1){
     74     if (i>1) insert(pos(i,j),pos(i-1,j),inf),insert(pos(i-1,j),pos(i,j),0);
     75     if (i<n) insert(pos(i,j),pos(i+1,j),inf),insert(pos(i+1,j),pos(i,j),0);
     76     if (j>1) insert(pos(i,j),pos(i,j-1),inf),insert(pos(i,j-1),pos(i,j),0);
     77     if (j<m) insert(pos(i,j),pos(i,j+1),inf),insert(pos(i,j+1),pos(i,j),0);
     78       }
     79   maxflow(S,T);
     80   for (RG int i=1,k=2;i<=n;++i)
     81     for (RG int j=1;j<=m;++j,k+=2)
     82       if (g[k].flow!=g[k].cap) return 0;
     83   return 1;
     84 }
     85 
     86 il void work(){
     87   n=gi(),m=gi(),tot=x=y=0,S=n*m+1,T=S+1;
     88   for (RG int i=1;i<=n;++i)
     89     for (RG int j=1;j<=m;++j){
     90       a[i][j]=gi(),tot=max(tot,a[i][j]);
     91       if ((i+j)&1) x+=a[i][j]; else y+=a[i][j];
     92     }
     93   if (n*m%2==0){
     94     if (x!=y){ puts("-1"); return; }
     95     RG ll l=tot,r=1LL<<50,mid;
     96     while (l<=r){
     97       mid=(l+r)>>1;
     98       if (check(mid)) ans=mid,r=mid-1; else l=mid+1;
     99     }
    100     printf("%lld
    ",(ans*n*m>>1)-x);
    101   } else{
    102     RG ll X=y-x; if (X<tot){ puts("-1"); return; }
    103     if (check(X)) printf("%lld
    ",X*((n*m)>>1)-x);
    104     else puts("-1");
    105   }
    106   return;
    107 }
    108 
    109 int main(){
    110 #ifndef ONLINE_JUDGE
    111   freopen("game.in","r",stdin);
    112   freopen("game.out","w",stdout);
    113 #endif
    114   RG int T=gi();
    115   while (T--) work();
    116   return 0;
    117 }
  • 相关阅读:
    bzoj4137[FJOI2015]火星商店问题
    HNOI2019游记
    bzoj4785:[ZJOI2017]树状数组:二维线段树
    快速傅里叶变换(FFT)
    动规大总结
    复习动规(4)
    复习动规(3)
    复习动规(2)
    复习动规(1)
    2019CSP-S游记(真)
  • 原文地址:https://www.cnblogs.com/wfj2048/p/7366236.html
Copyright © 2020-2023  润新知