原题:https://www.luogu.org/problemnew/show/P2765
题解:给了n根柱子,相当于最小路径覆盖是n,考虑用dinic,枚举i,将i拆成i和i+MAXN,S连i,i+MAXN连T。若i与j之间是完全平方关系,连接j与i+MAXN。跑一遍dinic,若不连通,相当于要在加一根柱子,若连通相当于能放在之前的柱子上。枚举到恰好用n根柱子时就行了。暴力输出就行了。
#include<bits/stdc++.h>
#define inf (1<<31)-1
using namespace std;
const int N=11000;
const int M=110000;
const int MAXN=2000;//maxn稍微大一点
struct E{int to,w,nxt;}data[M<<1];
int n,len,m,ans,S,T,to[N],h[N],tag[N],dep[N],cur[N];
int rt[N],cnt;
queue<int> q;
inline int rd(){
int x=0;int f=1;char s=getchar();
while(!isdigit(s)) f=(s=='-'?-1:f),s=getchar();
while(isdigit(s)) x=(x<<1)+(x<<3)+s-'0',s=getchar();
return x*f;
}
inline void ins(int x,int y,int w){
data[++len].to=y;data[len].w=w;data[len].nxt=h[x];h[x]=len;
data[++len].to=x;data[len].w=0;data[len].nxt=h[y];h[y]=len;
}
bool bfs(){
while(!q.empty()) q.pop();
memset(dep,0,sizeof dep);
dep[S]=1;q.push(S);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=h[x];i;i=data[i].nxt){
int y=data[i].to;int w=data[i].w;
if(!dep[y] && w!=0){
dep[y]=dep[x]+1;
q.push(y);
}
}
}
return dep[T];
}
int dfs(int x,int lim){
if(x==T) return lim;
for(int &i=cur[x];i;i=data[i].nxt){
int y=data[i].to;int w=data[i].w;
if(dep[y]==dep[x]+1 && w!=0){
int di=dfs(y,min(lim,w));
if(di){
to[x]=y;
if(x!=S) tag[max(y-MAXN,0)]=1;
data[i].w-=di;data[i^1].w+=di;
return di;
}
}
}
return 0;
}
int dinic(){
int ans=0;
while(bfs()){
for(int i=0;i<=T;i++) cur[i]=h[i];
while(int k=dfs(S,inf)) ans+=k;
}
return ans;
}
int main(){
n=rd();
S=0;T=5000;m=0;len=1;cnt=0;
while(cnt<=n){
++m;int x=m;int y=m+MAXN;
ins(S,x,1);ins(y,T,1);
for(int i=1;i<m;i++) if((int) sqrt(i+m)==sqrt(i+m)) ins(i,y,1);
ans=dinic();
if(ans==0) rt[++cnt]=m;
}
m--;//注意多做了一次
printf("%d\n",m);
for(int i=1;i<=n;i++){
printf("%d ",rt[i]);
int now=rt[i];
while(to[now] && to[now]!=T) printf("%d ",to[now]-MAXN),now=to[now]-MAXN;
printf("\n");
}
return 0;
}