题解 P2765 【魔术球问题】

mjtlyzbsy

2017-09-28 15:24:42

Solution

###这道题是一个非常好网络流题目 弱弱的说一句这是道题好像是网络流二十四题中的04 虽然可以用贪心或者一些玄学算法可以很快的跑过,但建议还是用网络流 并且网络流的建图真的很棒 **思路:** 我们先枚举球,一个个加到图里 #并在加边的过程是三个操作: - 当然不可缺少的是拆点把一个球x拆成x和x' - 下一步x连源点,x'连汇点都是容量为一的有向边 - 第三步是找这个数可以与那个数组成完全平方数,假设这个数为y,则y向x'连边 下面就是跑最大流了,在跑最大流的时候记录一下这个点向那个点流出流量(这就是代码中的nex[])![]( ![](https://cdn.luogu.com.cn/upload/pic/8289.png) ) **\_最后就是最让人期待的上代码环节了 ┗|`O′|┛ 嗷~~\_** ```cpp #include<cstdlib> #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<string> #include<queue> #include<vector> #include<cmath> using namespace std; const int INF=(1<<30); const int maxn=400000; int idx=0,e[maxn],f[maxn],ne[maxn],h[100000]; void add(int a,int b,int c){ e[idx]=b,ne[idx]=h[a],f[idx]=c,h[a]=idx++; e[idx]=a,ne[idx]=h[b],f[idx]=0,h[b]=idx++; } int S,T,ch[maxn],q[maxn],nex[maxn]; bool tell(){ memset(ch,-1,sizeof(ch)); int head=0,tail=0; ch[q[0]=S]=0; while(head<=tail){ int t=q[head++]; for(int i=h[t];i!=-1;i=ne[i]){ if(ch[e[i]]==-1&&f[i]){ ch[q[++tail]=e[i]]=ch[t]+1; } } } return ch[T]!=-1; } int zeng(int a,int b){ if(a==T)return b; int r=0; for(int i=h[a];i!=-1;i=ne[i]){ if(ch[a]+1==ch[e[i]]&&f[i]){ int t=zeng(e[i],min(b-r,f[i])); if(t>0)nex[a>>1]=(e[i]>>1); f[i]-=t;r+=t;f[i^1]+=t; } } if(!r)ch[a]=-1; return r; } int dinic(){ int r=0,t=0; while(tell()){ while(t=zeng(S,INF)){ r+=t; } } return r; } bool vis[maxn]; int w[1000]; int main(){ int n; S=0;T=10010; memset(h,-1,sizeof(h)); memset(nex,-1,sizeof(nex)); scanf("%d",&n); int now=0,num=0; while(now<=n){ ++num; add(S,num<<1,1);add((num<<1)|1,T,1); for(int i=sqrt(num)+1;i*i<(num<<1);i++)add((i*i-num)<<1,(num<<1)|1,1); int s=dinic(); if(s==0)w[++now]=num; } printf("%d\n",--num); memset(vis,false,sizeof(vis)); int k; for(int i=1;i<=n;i++){ if(!vis[w[i]]){ k=w[i];printf("%d ",k);vis[w[i]]=true; while(nex[k]!=-1&&nex[k]!=(T)>>1&&nex[k]!=0){ k=nex[k]; vis[k]=true; printf("%d ",k); } printf("\n"); } } } ``` 此题也可以用二分图做,希望能有dalao发一下二分图的做法