[網路流24題] 魔術球問題 (最大流)

weixin_30639719發表於2020-04-05

洛谷傳送門 LOJ傳送門

腦抽寫了個費用流$T$飛了,看了題解才明白是怎麼跑最大流的

這道題有一個貪心,如果小於它的數沒有能和它之和是完全平方數的,那麼它一定要新建一個柱子

證明可以看poorpool神犇的這篇部落格

由於每個點只能用一次,所以每個點$x$拆成$2$個點$x1$和$x2$,$x1$和源點相連,$x2$和匯點相連,流量均為$1$

而如果兩個數$x,y$能構成完全平方數,那麼$x1$連線$y2$

我們從小到大遍歷每個數$x$,然後在圖內加入$x2$,如果產生了一條新流,說明$x$能找到一個$y(y<x)$,$x+y$是完全平方數

否則,我們新加入一個柱子

然後不論能否產生新流,把$x1$加入到圖中

驗證是否產生新流可以用$Dinic$最大流實現

為什麼要這麼做呢?

我們是動態往圖裡加的點,每次加完點以後,如果情況合法,會產生一條新流。

如果不這麼做,我們每次要重新建邊,大大拉高的時間複雜度

如何記錄方案呢?

一個柱子裡的點會形成一條鏈(即使在網路流圖內它看起來不是一條鏈)。

可以向並查集一樣,開一個陣列,在$Dinic$裡記錄每個點的流量流向哪個位置就行了

  1 #include <cmath>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 #define N1 8010
  6 #define M1 40010
  7 #define ll long long
  8 #define dd double
  9 #define inf 0x3f3f3f3f
 10 using namespace std;
 11 
 12 int gint()
 13 {
 14     int ret=0,fh=1;char c=getchar();
 15     while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();}
 16     while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();}
 17     return ret*fh;
 18 }
 19 struct Edge{
 20 int head[N1],to[M1<<1],nxt[M1<<1],flow[M1<<1],cte;
 21 void ae(int u,int v,int F)
 22 {
 23     cte++; to[cte]=v; flow[cte]=F; 
 24     nxt[cte]=head[u]; head[u]=cte;
 25 }
 26 }e,E;
 27 int n,m,K,S,T,F,mx;
 28 int que[M1],hd,tl,dep[N1],cur[N1],pre[N1];
 29 int bfs()
 30 {
 31     int x,j,v;
 32     memset(dep,-1,sizeof(dep)); memcpy(cur,e.head,sizeof(cur));
 33     hd=1,tl=0; que[++tl]=S; dep[S]=0; 
 34     while(hd<=tl)
 35     {
 36         x=que[hd++];
 37         for(j=e.head[x];j;j=e.nxt[j])
 38         {
 39             v=e.to[j];
 40             if( e.flow[j]>0 && dep[v]==-1 )
 41                 dep[v]=dep[x]+1, que[++tl]=v;
 42         }
 43     }
 44     return dep[T]!=-1;
 45 }
 46 int dfs(int x,int limit)
 47 {
 48     int j,v,flow,ans=0; if(x==T||!limit) return limit;
 49     for(j=cur[x];j;j=e.nxt[j])
 50     {
 51         cur[x]=j; v=e.to[j];
 52         if( dep[v]==dep[x]+1 && (flow=dfs(v,min(e.flow[j],limit))) )
 53         {
 54             limit-=flow; ans+=flow;
 55             e.flow[j]-=flow; e.flow[j^1]+=flow;
 56             if(v>n*n&&v<=2*n*n) pre[x]=v-n*n;
 57             if(!limit) break;
 58         }
 59     }
 60     return ans;
 61 }
 62 
 63 int stk[N1],tp;
 64 
 65 int de;
 66 int Dinic()
 67 {
 68     int mxflow=0;
 69     while(bfs())
 70         mxflow+=dfs(S,inf);
 71     return mxflow;
 72 }
 73 void solve()
 74 {
 75     int i,j,k,sq,num=0,ans,x;
 76     S=0; T=2*mx+1; e.cte=1;
 77     for(i=1;i<=mx;i++)
 78     {
 79         //e.ae(i,i+mx,1); e.ae(i+mx,i,0);
 80         for(j=n;j>1;j--)
 81         {
 82             k=j*j-i;
 83             if(k>0&&k<i) 
 84                 e.ae(k,i+mx,1),e.ae(i+mx,k,0);
 85         }
 86         e.ae(i+mx,T,1); e.ae(T,i+mx,0); 
 87         if(Dinic()<=0){
 88             if(num+1>n){ ans=i-1; break;}
 89             stk[++tp]=i; num++;
 90         }
 91         e.ae(S,i,1); e.ae(i,S,0); 
 92     }
 93     printf("%d\n",ans);
 94     for(i=1;i<=n;i++)
 95     {
 96         x=stk[i]; 
 97         while(x) printf("%d ",x), x=pre[x];
 98         puts("");
 99     }
100 }
101 
102 int main()
103 {
104     scanf("%d",&n); mx=n*n;
105     solve();
106     return 0;
107 }

 

轉載於:https://www.cnblogs.com/guapisolo/p/10297663.html

相關文章