【AtCoder Regular Contest 085 E】MUL
最小割。
题目大意:
给定 $a_1,a_2,a_3,\ldots,a_n$,你可以执行以下操作:
- 选择一个正整数 $x$,将编号为 $x$ 的倍数的数删去。
要求出剩下的数的和最大是多少。
题解:
如果所有数都是正数,那么有一个最小割模型:
从源点连向 $n$ 个点容量 $0$ 的边,表示选择删除的 $x$。
从 $n$ 个代表原数的点向汇点连容量为 $a_i$ 的边。
从代表删除倍数的点连向它能删除的所有代表原数的点链容量 $+\infty$ 的边。
然后最小割就是最少要从总量中删去的值。
这个图在 $a_i$ 为正时显然是 $0$,但是在 $a_i$ 出现负数的时候,就没法直接做了。
不过我们可以发现,任意一个合法的最小割,都要割掉恰好 $n$ 条边。
所以我们给每条边都赋一个附加权值,让它们变为非负,最后再减掉即可。
Code:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
#define int long long
const int N=1919810;
struct edge{
int to,nxt,cap;
}e[N];
int head[233],cnt=1,T,n,iter[233],dep[N],ans;
inline void addedge(int u,int v,int w){
e[++cnt]=(edge){v,head[u],w},head[u]=cnt;
e[++cnt]=(edge){u,head[v],0},head[v]=cnt;
}
queue<int>q;
void bfs(){
dep[0]=1;
for(q.push(0);!q.empty();){
int u=q.front();q.pop();
for(int i=head[u];i;i=e[i].nxt)
if(e[i].cap&&!dep[e[i].to]){
dep[e[i].to]=dep[u]+1;
q.push(e[i].to);
}
}
}
int dfs(int u,int f){
if(!f||u==T)return f;
for(int&i=iter[u];i;i=e[i].nxt)
if(e[i].cap&&dep[e[i].to]>dep[u]){
int d=dfs(e[i].to,min(f,e[i].cap));
if(d){
e[i].cap-=d,e[i^1].cap+=d;
return d;
}
}
return 0;
}
int dinic(){
for(int flow=0;;){
memset(dep,0,sizeof dep);
bfs();
if(!dep[T])return flow;
memcpy(iter,head,sizeof head);
while(int f=dfs(0,1145141919810))flow+=f;
}
}
main(){
scanf("%lld",&n);
T=2*n+1;
for(int i=1,x;i<=n;++i)
scanf("%lld",&x),ans+=x,addedge(i,T,x+1e9);
for(int i=1;i<=n;++i){
addedge(0,i+n,1e9);
for(int j=i;j<=n;j+=i)
addedge(i+n,j,1145141919810);
}
printf("%lld\n",ans-dinic()+(int)1e9*n);
return 0;
}