链接:https://www.luogu.org/problemnew/show/P2765
思路:利用网络流可以在残留网络上跑的性质,我们考虑边跑网络流边加点,当当前点数-二分图的最大匹配数(将一个点拆为两个分别放在x和y上进行二分匹配,详见最小路径覆盖问题)>n时表示最小路径覆盖的数目超过了n,这时候停止达到球的最大值,最后出来查看一下所有的正向边flow不为0的,将每个点的下一个点记录出来,最后输出路径即可。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m;
const int maxn = 5010;
const int INF = 1e9;
int vis[maxn];
int nexto[maxn];
struct edge{
int from,to,cap,flow;
};
struct Dinic{
int n,m,s,t;
vector<edge> edges;
vector<int> G[maxn];
bool vis[maxn];
int d[maxn];
int cur[maxn];
void init(int n){
this->n = n;
edges.clear();
for(int i=0;i<=n;i++)G[i].clear();
}
void addedge(int from,int to,int cap){
edges.push_back(edge{from,to,cap,0});
edges.push_back(edge{to,from,0,0});
m = edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool bfs(){
memset(vis,0,sizeof(vis));
queue<int> q;
q.push(s);
d[s] = 0;
vis[s] = 1;
while(!q.empty()){
int x = q.front();
q.pop();
for(int i=0;i<G[x].size();i++){
edge &e = edges[G[x][i]];
if(!vis[e.to]&&e.cap>e.flow){
vis[e.to] = 1;
d[e.to] = d[x] + 1;
q.push(e.to);
}
}
}
return vis[t];
}
int dfs(int x,int a){
if(x==t||a==0)return a;
int flow = 0,f;
for(int &i = cur[x];i<G[x].size();i++){
edge &e = edges[G[x][i]];
if(d[x] + 1 == d[e.to]&&(f=dfs(e.to,min(a,e.cap-e.flow)))>0){
e.flow+=f;
edges[G[x][i]^1].flow -=f;
flow+=f;
a-=f;
if(a==0)break;
}
}
return flow;
}
int maxflow(int s,int t){
this->s = s;
this->t = t;
int flow = 0;
while(bfs()){
memset(cur,0,sizeof(cur));
flow+=dfs(s,INF);
}
return flow;
}
}solver;
set<int> all;
int main(){
scanf("%d",&n);
for(int i=0;i<=100;i++)all.insert(i*i);
solver.init(5000);
int now = 0;
int res = 0;
while(++now){
solver.addedge(0,now,1);
solver.addedge(now+2000,5000,1);//2000作为x和y的分界线
for(int i=1;i<now;i++){
if(all.count(i+now))
solver.addedge(i,now+2000,1);
}
res+=solver.maxflow(0,5000);
//printf("%d\n",res);
if(now-res>n)break;
}
printf("%d\n",now-1);
for(int i=0;i<solver.edges.size();i+=2){
edge e = solver.edges[i];
if(e.flow)nexto[e.from] = e.to>2000?e.to-2000:e.to;
}
for(int i=1;i<=now-1;i++){
int u = i;
if(!vis[u]){
printf("%d",u);
vis[u] = 1;
u = nexto[u];
while(u){
vis[u] = 1;
printf(" %d",u);
u = nexto[u];
}
printf("\n");
}
}
return 0;
}