脑抽写了个费用流$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 ",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 }