传送门
魔术球
题意:向n根柱子里依次放置编号连续且递增的球。且同一根柱子里相邻两球编号和为完全平方数。求在这n根柱子里最多能放多少球。
I think
模型转化:视n为路径覆盖数,枚举放入环中数的数量,转化为上一题的路径覆盖问题。
由于“依次”放球,所以构造出的一定是有向无环图。
Code
每次重新建边,不加优化的版本
#include<vector>
#include<cstdio>
#include<queue>
#include<cmath>
using namespace std;
const int sm = 4000;
const int sn = 127200;
const int C = 1600;
const int Inf = 0x3f3f3f3f;
int a,N,S=3201,T=3202;
int cnt,sum,ans,tot=1;
int to[sn],nxt[sn],hd[sm],w[sn];
int p[sm],lev[sm],cur[sm]; bool pf[3605];
vector<int>now[sm>>1],c[sm];
int Min(int x,int y) { return x<y?x:y; }
void Add(int u,int v) {
to[++tot]=v,nxt[tot]=hd[u],hd[u]=tot,w[tot]=1;
to[++tot]=u,nxt[tot]=hd[v],hd[v]=tot,w[tot]=0;
}
void Init(int *g) {
for(int i=1;i<=a;++i) g[i]=0;
for(int i=C+1;i<=C+a;++i) g[i]=0;
g[S]=g[T]=0;
}
bool Bfs() {
queue<int>q;
int t; q.push(S);
Init(lev);lev[S]=1;
while(!q.empty()) {
t=q.front(),q.pop();
for(int i=hd[t];i;i=nxt[i])
if(!lev[to[i]]&&w[i]) {
lev[to[i]]=lev[t]+1;
if(to[i]==T) return 1;
q.push(to[i]);
}
}
return 0;
}
int Dfs(int x,int mx) {
if(x==T||!mx) return mx;
int f;
for(int i=cur[x]?cur[x]:hd[x];i;i=nxt[i]) {
cur[x]=i;
if(lev[to[i]]==lev[x]+1&&w[i]) {
if(f=Dfs(to[i],Min(mx,w[i])))
return w[i]-=f,w[i^1]+=f,f;
}
}
return 0;
}
int Dinic() {
int f,Flw=0;
while(Bfs()) {
Init(cur);
while(f=Dfs(S,Inf))
Flw+=f;
}
return Flw;
}
void Pre() {
for(int i=hd[S];i;i=nxt[i])
if(!w[i]) {
for(int j=hd[to[i]];j;j=nxt[j])
if(!w[j]&&to[j]!=S) {
p[to[j]]=to[i];
p[to[i]]=to[j];
break;
}
}
}
int main() {
int u,v,ret=0;
scanf("%d",&N);
for(int i=1;i<=60;++i) pf[i*i]=1;
while(1) {
++a;//枚举
Init(hd);
for(int i=1;i<a;++i)
if(pf[i+a]) c[i].push_back(a);
tot=1;
for(int i=1;i<=a;++i) {//重新建边
Add(S,i),Add(i+C,T);
for(int j=0;j<c[i].size();++j)
Add(i,c[i][j]+C);
}
ret=Dinic();
if(a-ret==N) {//预备储存答案直到a-ret==N+1,此时取到最大答案
Pre();
ans=sum=0;
for(int i=1;i<=a;++i)
if(p[i]) {
now[++sum].clear();
now[sum].push_back(u=i);
do {
v=u,u=p[u]-C;
now[sum].push_back(u);
p[v]=0;
}while(p[u]);
ans+=now[sum].size();
}
}
else
if(a-ret==N+1) break;
}
printf("%d\n",ans);
for(int i=1;i<=sum;++i) {
printf("%d",now[i][0]);
for(int j=1;j<now[i].size();++j)
printf(" %d",now[i][j]);
putchar(10);
}
return 0;
}
每次只在Dinic之后更新容量的优化版本,虽说从实际运行时间上来看优化不大,还是要mark一下这种将数组指定位复制的memcpy用法。
复制b[u..v]到a[x..y](其中y-x==v-u)
memcpy(a+x, b+u, sizeof(int)*(v-u+1));
#include<vector>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
using namespace std;
const int sm = 4000;
const int sn = 127200;
const int C = 1600;
const int Inf = 0x3f3f3f3f;
int a,N,S=3201,T=3202;
int cnt,sum,ans,tot=1;
int to[sn],nxt[sn],hd[sm],w[sn],_w[sn];
int p[sm],lev[sm],cur[sm]; bool pf[3605];
vector<int>now[sm>>1],c[sm];
int Min(int x,int y) { return x<y?x:y; }
void Add(int u,int v) {
to[++tot]=v,nxt[tot]=hd[u],hd[u]=tot,_w[tot]=w[tot]=1;
to[++tot]=u,nxt[tot]=hd[v],hd[v]=tot,_w[tot]=w[tot]=0;
}
void Init(int *g) {
for(int i=1;i<=a;++i) g[i]=0;
for(int i=C+1;i<=C+a;++i) g[i]=0;
g[S]=g[T]=0;
}
bool Bfs() {
queue<int>q;
int t; q.push(S);
Init(lev);lev[S]=1;
while(!q.empty()) {
t=q.front(),q.pop();
for(int i=hd[t];i;i=nxt[i])
if(!lev[to[i]]&&w[i]) {
lev[to[i]]=lev[t]+1;
if(to[i]==T) return 1;
q.push(to[i]);
}
}
return 0;
}
int Dfs(int x,int mx) {
if(x==T||!mx) return mx;
int f;
for(int i=cur[x]?cur[x]:hd[x];i;i=nxt[i]) {
cur[x]=i;
if(lev[to[i]]==lev[x]+1&&w[i]) {
if(f=Dfs(to[i],Min(mx,w[i])))
return w[i]-=f,w[i^1]+=f,f;
}
}
return 0;
}
int Dinic() {
int f,Flw=0;
while(Bfs()) {
Init(cur);
while(f=Dfs(S,Inf))
Flw+=f;
}
return Flw;
}
void Pre() {
for(int i=hd[S];i;i=nxt[i])
if(!w[i]) {
for(int j=hd[to[i]];j;j=nxt[j])
if(!w[j]&&to[j]!=S) {
p[to[j]]=to[i];
p[to[i]]=to[j];
break;
}
}
}
int main() {
int u,v,ret=0;
scanf("%d",&N);
for(int i=1;i<=60;++i) pf[i*i]=1;
while(1) {
++a;
memcpy(w+2,_w+2,sizeof(int)*(tot-1));
for(int i=1;i<a;++i)
if(pf[i+a]) Add(i,a+C);
Add(S,a),Add(a+C,T);
ret=Dinic();
if(a-ret==N) {
Pre();
ans=sum=0;
for(int i=1;i<=a;++i)
if(p[i]) {
now[++sum].clear();
now[sum].push_back(u=i);
do {
v=u,u=p[u]-C;
now[sum].push_back(u);
p[v]=0;
}while(p[u]);
ans+=now[sum].size();
}
}
else
if(a-ret==N+1) break;
}
printf("%d\n",ans);
for(int i=1;i<=sum;++i) {
printf("%d",now[i][0]);
for(int j=1;j<now[i].size();++j)
printf(" %d",now[i][j]);
putchar(10);
}
return 0;
}