题面
考虑第x个数没有被筛去,那么x的约数都应该没有被筛去。
所以每个点的点权就是它的价值,然后向自己所有约数连边,跑最大权闭合子图即可。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
int n,a[110],S,T,ne[110],dl[110];
bool vis[110];
ll ans=0;
struct edge
{
int t;
ll c,f;
edge *next,*rev;
}*con[110];
void ins(int x,int y,ll c)
{
edge *p=new edge;
p->t=y;
p->c=c;
p->f=0;
p->next=con[x];
con[x]=p;
p=new edge;
p->t=x;
p->c=0;
p->f=0;
p->next=con[y];
con[y]=p;
con[x]->rev=con[y];
con[y]->rev=con[x];
}
bool bfs()
{
int head,tail;bool re=0;
memset(ne,0,sizeof(ne));
dl[1]=S;ne[S]=1;
for(head=tail=1;head<=tail;head++)
{
int v=dl[head];
if(v==T) re=1;
for(edge *p=con[v];p;p=p->next)
if(!ne[p->t]&&p->c>p->f)
ne[p->t]=ne[v]+1,dl[++tail]=p->t;
}
return re;
}
ll dinic(int v,ll flow)
{
if(v==T) return flow;
if(vis[v]) return 0;
ll re=0;
for(edge *p=con[v];flow&&p;p=p->next)
if(ne[p->t]==ne[v]+1&&p->c>p->f)
{
ll o=dinic(p->t,min(flow,p->c-p->f));
p->f+=o;p->rev->f-=o;
re+=o;flow-=o;
}
if(re==0) vis[v]=1;
return re;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
S=0;T=n+1;
for(int i=1;i<=n;i++)
if(a[i]>0) ins(S,i,a[i]),ans+=a[i];
else
{
ins(i,T,-a[i]);
for(int j=2;i*j<=n;j++)
if(a[i*j]>0) {ins(i*j,i,1e15);}
}
while(bfs())
{
memset(vis,0,sizeof(vis));
ans-=dinic(S,1e15);
}
printf("%lld",ans);
return 0;
}