题目大意:
假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为1,2,3,…的球。
(1)每次只能在某根柱子的最上面放球。
(2)在同一根柱子中,任何2个相邻球的编号之和为完全平方数。
试设计一个算法,计算出在n根柱子上最多能放多少个球。例如,在4 根柱子上最多可放11 个球。
对于给定的n,计算在n根柱子上最多能放多少个球。
思路:
其实我一开始的思路是这样的:把可以放在一起的球之间连一条流量为1的边,然后每个球拆成两个点,中连一条流量为1 的费用为-1的边,然后其实我们要求的就是在流量限制为n下的最小费用。然后发现这种方法太不正常了。
其实正解是转化为了图论的模型的,先假设每个球单独放在一根柱子上面,然后再来合并球,求最大的合并次数。两个球之间连有向边了之后,就是一个最小路径覆盖的模型了。然后就转化为二分图跑最大流就好了。
至于输出方案的话,就把流满的边找出来乱搞一下就好了。
/*========================
* Author : ylsoi
* Problem : luogu2765
* Algorithm : Max_Flow
* Time : 2018.7.13
* ========================*/
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a;i<=b;++i)
typedef long long ll;
using namespace std;
void File(){
freopen("luogu2765.in","r",stdin);
freopen("luogu2765.out","w",stdout);
}
const int maxn=55+10;
const int maxm=5e4+10;
const int inf=INT_MAX;
int n,ss,tt,tot,ans;
int beg[maxm],las[maxm<<1],to[maxm<<1],flow[maxm<<1],cnte=1;
void add(int u,int v,int f){
las[++cnte]=beg[u]; beg[u]=cnte; to[cnte]=v; flow[cnte]=f;
las[++cnte]=beg[v]; beg[v]=cnte; to[cnte]=u; flow[cnte]=0;
}
struct dinic{
int cur[maxm],num[maxm],Max_flow;
queue<int>qu;
bool bfs(){
memset(num,0,sizeof(num));
qu.push(ss); num[ss]=1;
while(qu.size()){
int u=qu.front(); qu.pop();
for(int i=beg[u];i;i=las[i]){
if(!flow[i] || num[to[i]])continue;
num[to[i]]=num[u]+1;
qu.push(to[i]);
}
}
return num[tt];
}
int dfs(int u,int gap){
if(u==tt || !gap)return gap;
int sum=0,f;
for(int &i=cur[u];i;i=las[i]){
if(num[to[i]]!=num[u]+1)continue;
if((f=dfs(to[i],min(flow[i],gap)))){
gap-=f;
sum+=f;
flow[i]-=f;
flow[i^1]+=f;
}
if(!gap)break;
}
return sum;
}
bool cal(){
while(bfs()){
REP(i,ss,tot)cur[i]=beg[i];
Max_flow+=dfs(ss,inf);
}
return ans-Max_flow<=n;
}
}T;
int fa[maxm];
int find(int x){return fa[x]==x ? x : fa[x]=find(fa[x]);}
void work(){
ss=1; tt=2; tot=2;
while(T.cal()){
++ans;
add(ss,tot+1,1);
add(tot+2,tt,1);
for(int i=1;i*i<2*ans;++i)
if(i*i-ans>0)
add(tot+1,(i*i-ans+1)*2,1);
tot+=2;
}
ans=ans-1;
printf("%d\n",ans);
REP(i,1,ans)fa[i]=i;
REP(i,1,ans)
for(int j=beg[i*2+1];j;j=las[j])
if(to[j]%2==0 && to[j]>=4 && !flow[j])
fa[find(i)]=find(to[j]/2-1);
set<int>s[maxm];
REP(i,1,ans)s[find(i)].insert(i);
set<int>::iterator it;
REP(i,1,ans)if(s[i].size()){
for(it=s[i].begin();it!=s[i].end();++it)
printf("%d ",*it);
putchar('\n');
}
}
int main(){
File();
scanf("%d",&n);
work();
return 0;
}