洛谷P2765 魔术球问题(最大流)

传送门

%%%KSkun大佬

话说明明是网络流……这题竟然还有打表找规律和纯贪心AC的……都是神犇啊……

来说一下如何建图。首先把每一个点拆成$X_i$和$Y_i$,然后$S$向$X_i$连一条容量为$1$的边,$Y_i$向$T$连一条容量为$1$的边。对于能和它组成完全平方数的点,从$A_j$向$B_i$连一条容量为$1$的边

然后考虑一下,加球不会导致柱子减少,所以可以枚举球数,然后每次加一个球,并跑一次最大流。如果新加入的球是能加到某一个柱子中的,那么这一次跑最大流是能得到新流的,只要能一直得到新流就一直加,当不能的时候,将柱子数加一(即将这一个球放到新的柱子上)

当柱子数等于$n+1$的时候退出,因为第$n+1$根柱子是刚被加的,所以在放最后一个球之前,所有的球都能放在$n$根柱子中,那么当前的球数减一就是答案

然后考虑一下怎么求方案。对于每一个点,我们可以定义$X_i=i*2,Y_i=i*2+1$,那么可以保证任意两个点的$X_i$和$Y_i$不会重复,且不管在哪一个点,除以二之后就是原来的点。那么我们可以在跑$dfs$的时候,把每一个可以往下走增广路的点连边(这就说明他们相加是完全平方数,可以放在同一个柱子里),那么就可以形成一个类似链表的东西。记录一下每一根柱子一开始放的球,然后沿着链表去找这个柱子上有哪些球就可以了

 1 //minamoto
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<algorithm>
 5 #include<cstring>
 6 #include<queue>
 7 #include<cmath>
 8 using namespace std;
 9 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
10 char buf[1<<21],*p1=buf,*p2=buf;
11 inline int read(){
12     #define num ch-'0'
13     char ch;bool flag=0;int res;
14     while(!isdigit(ch=getc()))
15     (ch=='-')&&(flag=true);
16     for(res=num;isdigit(ch=getc());res=res*10+num);
17     (flag)&&(res=-res);
18     #undef num
19     return res;
20 }
21 char sr[1<<21],z[20];int C=-1,Z;
22 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
23 inline void print(int x){
24     if(C>1<<20)Ot();
25     while(z[++Z]=x%10+48,x/=10);
26     while(sr[++C]=z[Z],--Z);
27 }
28 const int inf=0x3f3f3f3f,N=100005,M=400005;
29 int head[N],Next[M],ver[M],edge[M],tot=1,Pre[N];
30 inline void add(int u,int v,int e){
31     ver[++tot]=v,Next[tot]=head[u],head[u]=tot,edge[tot]=e;
32     ver[++tot]=u,Next[tot]=head[v],head[v]=tot,edge[tot]=0;
33 }
34 int dep[N],s,t;
35 int n,m,cnt;
36 queue<int> q;
37 bool bfs(){
38     memset(dep,-1,sizeof(dep));
39     dep[s]=0,q.push(s);
40     while(!q.empty()){
41         int u=q.front();q.pop();
42         for(int i=head[u];i;i=Next[i]){
43             int v=ver[i];
44             if(dep[v]<0&&edge[i])
45             dep[v]=dep[u]+1,q.push(v);
46         }
47     }
48     return ~dep[t];
49 }
50 int dfs(int u,int limit){
51     if(!limit||u==t) return limit;
52     int flow=0,f;
53     for(int i=head[u];i;i=Next[i]){
54         int v=ver[i];
55         if(dep[v]==dep[u]+1&&(f=dfs(v,min(limit,edge[i])))){
56             flow+=f,limit-=f;
57             edge[i]-=f,edge[i^1]+=f;
58             Pre[u>>1]=v>>1;
59             if(!limit) break;
60         }
61     }
62     return flow;
63 }
64 int dinic(){
65     int flow=0;
66     while(bfs()) flow+=dfs(s,inf);
67     return flow;
68 }
69 bool vis[N];int w[65];
70 int main(){
71     n=read();
72     s=0,t=10005;
73     while(m<=n){
74         ++cnt;
75         add(s,cnt<<1,1),add(cnt<<1|1,t,1);
76         for(int i=sqrt(cnt)+1;i*i<(cnt<<1);++i) add((i*i-cnt)<<1,cnt<<1|1,1);
77         int s=dinic();
78         if(!s) w[++m]=cnt;
79     }
80     print(--cnt),sr[++C]=10;
81     for(int i=1;i<=n;++i){
82         int u=w[i];
83         if(!vis[u]){
84             print(u),sr[++C]=32,vis[u]=1;
85             while(Pre[u]&&Pre[u]!=t>>1){
86                 u=Pre[u];
87                 vis[u]=1;
88                 print(u),sr[++C]=32;
89             }
90             sr[++C]=10;
91         }
92     }
93     Ot();
94     return 0;
95 }

猜你喜欢

转载自www.cnblogs.com/bztMinamoto/p/9498668.html