洛谷 P2765 魔术球问题 解题报告
思路比较有趣,也比较新颖。
但想通了其实ok。
IDEA:
- 首先,很容易想到球数和柱子数正相关。因为加球一定不会使柱子数减少。
- 这样的话,我们从第一个球开始加入图中,每一个球拆成入点和出点,s->i*2=1,i*2+1->t=1 (用i*2和i*2+1表示的原因是:动态加点不知道总球数)
- 然后,每加一个新的球,我们试图让它和之前的球连边:
- 设这个球是now,设平方和为 i*i。则之前的球为i*i-now。
- 显然0<i*i-now<now,即now<i*i<2*now。所以i从sqrt(now)+1开始枚举,(i*i-now)*2->now*2+1连边。
- 每次连边后跑dinic,如果有增流则说明不需要添加新柱子,能放在之前的球上。
- 如果没有则说明新的球没有地方放了,加一个柱子,当柱子数超过n的时候退出循环,柱子数--。
- 每次加柱子时记录下这个柱子最下面放的球(也就是当前球)。
- 输出时,枚举每个柱子,从最下面的球开始递归找球输出。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> #include <queue> #define ll long long #define space putchar(' ') #define endl putchar('\n') #define debug puts("------------------------") #define F(i,x,n) for(int i=x;i<=n;++i) #define F_(i,x,n) for(int i=x;i>=n;--i) using namespace std; inline void read(int &a) {a=0;int c=getchar(),b=1; while(c>'9'||c<'0') {if(c=='-')b=-1;c=getchar();} while(c>='0'&&c<='9') a=(a<<3)+(a<<1)+c-48,c=getchar();a*=b; } inline int Rem() {int a=0,c=getchar(),b=1; while(c>'9'||c<'0') {if(c=='-')b=-1;c=getchar();} while(c>='0'&&c<='9') a=(a<<3)+(a<<1)+c-48,c=getchar();return a*=b; } inline void write(int x) {if(x>9)write(x/10);putchar('0'+x%10);} inline void W(int x) {if(x<0){putchar('-'),x=-x;}write(x);} /**/ const int N=4000,inf=0x3f3f3f3f; int head[N],cnt=1,cur[N],n,s,t,dep[N],maxflow; int num,rec[N],now; bool vis[N]; char a,b; struct edge { int nxt,to,c; }e[N*N]; /**/ namespace DINIC { inline void add_one(int u,int v,int c) { e[++cnt]=(edge){head[u],v,c}; head[u]=cnt; } inline void add(int u,int v,int c) { add_one(u,v,c); add_one(v,u,0); } inline int bfs() { memset(vis,0,sizeof(vis)); memset(dep,0x3f,sizeof(dep)); memcpy(cur,head,sizeof(head)); queue<int>q; q.push(s); dep[s]=0,vis[s]=1; while(!q.empty()) { int u=q.front();q.pop();vis[u]=0; for(int i=head[u];i!=-1;i=e[i].nxt) { int v=e[i].to; if(dep[v]>dep[u]+1&&e[i].c) { dep[v]=dep[u]+1; if(!vis[v]) { vis[v]=1; q.push(v); } } } } return dep[t]!=inf; } int dfs(int u,int flow) { if(u==t) { vis[t]=1; maxflow+=flow; return flow; } int used=0,minflow=0; for(int i=cur[u];i!=-1;i=e[i].nxt) { int v=e[i].to; if(e[i].c&&dep[v]==dep[u]+1) { if((minflow=dfs(v,min(flow-used,e[i].c)))!=0) { used+=minflow; e[i].c-=minflow; e[i^1].c+=minflow; if(used==flow) break; } } } return used; } int Dinic() { maxflow=0; while(bfs()) { vis[t]=1; while(vis[t]) { vis[t]=0; dfs(s,inf); } } return maxflow; } }using namespace DINIC; void out(int u) { for(int i=head[u];i;i=e[i].nxt) { int v=e[i].to; if(v!=s&&v!=t&&e[i].c==0) { cout<<(v-1)/2<<' '; out((v-1)/2*2); } } } int main() { memset(head,-1,sizeof(head)); read(n); s=0,t=1; while(num<=n) { ++now; add(s,now*2,1); add(now*2+1,t,1); for(int i=sqrt(now)+1;i*i<2*now;i++) { add((i*i-now)*2,now*2+1,1); } if(!Dinic()) rec[++num]=now; } --now; cout<<now<<'\n'; for(int i=1;i<=n;i++) { cout<<rec[i]<<' ';out(rec[i]*2);endl; } return 0; }