AT3672 mul
一道比较有意思的网络流。
把所有数的倍数连权为\(inf\)的边,
然后把所有正数连汇点,所有负数连原点。
最后减去最小割就是答案。
易证。
Code:
#include <bits/stdc++.h>
#define int long long
const int maxn = 110;
const int inf = 0x3f3f3f3f;
typedef long long ll;
int n, m, i, j, k, st, ed, cnte = 1;
ll hd[maxn], cur[maxn], a[maxn], dep[maxn];
ll nxt[30000], ver[30000], wei[30000];
ll ans;
template<class t> inline t _min(t a,t b) { return a < b ? a : b; }
template<class t> inline void read(t& res) {
res = 0; char ch = getchar(); bool sign = 0;
while(!isdigit(ch))
sign |= ch == '-', ch = getchar();
while(isdigit(ch))
res = (res << 1) + (res << 3) + (ch & 15), ch = getchar();
if(sign)
res = -res;
}
inline void adde(int u,int v,ll w) {
ver[++cnte] = v; nxt[cnte] = hd[u]; wei[cnte] = w; hd[u] = cnte;
ver[++cnte] = u; nxt[cnte] = hd[v]; wei[cnte] = 0; hd[v] = cnte;
}
inline bool bfs() {
std::queue<int> q;
memset(dep,0,sizeof(dep));
dep[st] = 1; q.push(st);
while(!q.empty()) {
int u = q.front(); q.pop();
for(int i = hd[u];~i;i = nxt[i]) {
int v = ver[i]; ll w = wei[i];
if(!dep[v] && w) {
dep[v] = dep[u] + 1;
q.push(v);
}
}
}
return dep[ed];
}
ll dfs(int u,ll flow) {
if(u == ed)
return flow;
ll dec = flow;
for(ll& i = cur[u];~i && dec;i = nxt[i]) {
int v = ver[i]; ll w = wei[i];
if(dep[u] + 1 == dep[v] && w) {
int tmp = dfs(v,_min(w,dec));
dec -= tmp;
wei[i] -= tmp;
wei[i ^ 1] += tmp;
}
}
return flow - dec;
}
inline void dinic() {
while(bfs()) {
memcpy(cur,hd,sizeof(hd)); ll flow;
while(flow = dfs(st,inf))
ans -= flow;
}
return;
}
signed main() {
memset(hd,-1,sizeof(hd));
read(n);
st = n + 1; ed = n + 2;
for(int i = 1;i <= n;i++)
read(a[i]);
for(int i = 1;i <= n;i++) {
if(a[i] >= 0)
adde(i,ed,a[i]), ans += a[i];
else
adde(st,i,-a[i]);
}
for(int i = 1;i <= n;i++)
for(int j = i << 1;j <= n;j += i)
adde(i,j,inf);
dinic();
printf("%lld\n",ans);
return 0;
}