洛谷P2765 魔術球問題 網路流之最小路徑覆蓋
阿新 • • 發佈:2018-12-16
P2756P2756
思路:k根柱子相當於k條路徑覆蓋,求最多能放n個球的n的大小。 根據每個球相鄰球必須相加為平方數可以建邊,然後由: 最小路徑覆蓋 = n - 最大二分匹配, 可以列舉n,也可以二分查詢(因為球數n隨柱子k的增大是單調不遞減的)。 這樣可以得到球數n的大小。 然後輸出每條簡單路,和求最小路徑覆蓋一樣,每次增廣成功記錄連線的點就行了。 Code:
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int AX = 1e4 + 666 ;
int n ;
int cur[ AX] ;
int head[AX] ;
int d[AX] ;
int nxt[AX];
int s = 0 , t = 6666 ;
struct Node{
int u , v , flow , next1 ;
Node( int u = 0 , int v = 0 , int flow = 0 , int next1 = 0 ):u(u),v(v),flow(flow),next1(next1){}
}G[AX*10];
int tot ;
void addEdge( int u , int v , int w ){
G[tot] = Node( u , v , w , head[u] ) ; head[u] = tot++ ;
G[tot] = Node( v , u , 0 , head[v] ) ; head[v] = tot++ ;
}
bool bfs(){
memset( d , -1 , sizeof(d) ) ;
d[s] = 0 ;
queue<int>q;
q.push(s);
while( !q.empty() ) {
int u = q.front() ;
q.pop() ;
for( int i = head[u] ; ~i ; i = G[i].next1 ) {
int v = G[i].v ;
if ( d[v] == -1 && G[i].flow ){
d[v] = d[u] + 1 ;
q.push(v) ;
if( v == t ) return true;
}
}
}
return ~d[t] ;
}
int dfs( int u , int cap ){
if( u == t || !cap ) return cap ;
int r = 0 ;
for( int i = cur[u] ; ~i ; i = G[i].next1 ){
int v = G[i].v ;
if( G[i].flow && d[v] == d[u] + 1 ){
int tmp = min( G[i].flow , cap - r ) ;
cur[u] = i ;
tmp = dfs( v , tmp );
r += tmp ;
if( tmp ){
nxt[u] = v ;
}
G[i].flow -= tmp ;
G[i^1].flow += tmp ;
if( r == cap ) break ;
}
}
if( !r ) d[u] -= 2 ;
return r ;
}
int Dinic(){
int cnt = 0 ;
int tmp ;
while( bfs() ){
memcpy( cur , head , sizeof(head) ) ;
while( tmp = dfs( s , INF ) ) cnt += tmp ;
}
return cnt ;
}
int k ;
bool solve( int n ) {
tot = 0 ;
memset( nxt , -1 , sizeof(nxt) );
memset( head , -1 , sizeof(head) ) ;
for( int i = 1 ; i <= n ; i++ ){
for( int j = i + 1 ; j <= n ; j++ ){
int x = sqrt(i + j) ;
if( x * x == ( i + j ) ) {
addEdge( i , n + j , 1 ) ;
}
}
}
for( int i = 1 ; i <= n ; i++ ){
addEdge( s , i , 1 ) ;
addEdge( i + n , t , 1 ) ;
}
int flow = Dinic() ;
n -= flow ;
return ( k < n ) ;
}
int main(){
scanf("%d",&k) ;
int l = 0 , r = 1600 ;
while( l < r ){
int mid = ( l + r ) / 2 ;
if( solve(mid) ){
r = mid ;
}else l = mid + 1 ;
}
n = l - 1 ;
solve( n ) ;
printf("%d\n",n);
for( int i = 1 ; i <= n ; i++ ){
if( nxt[i] == -1 ) continue ;
int u = i ;
while( ~u ){
if( u >= n ) u -= n ;
printf("%d ",u);
int x = nxt[u];
nxt[u] = -1;
u = x ;
}
printf("\n");
}
return 0 ;
}