題目鏈接:這裡傳送
題目大意:給定一個n個數的序列,標號為1~n,有正有負,可以無數次操作:刪去一些數,條件是刪去編號為i的數同時,所有編號是i的整數倍的數都要被刪去。求剩下的數的和最大時的和,即剩下的sum最大。
解題思路:典型的最大權閉合子圖問題,有關知識的詳細可參考:我覺得最能看懂的博文
簡要介紹:如果選一個x,就必須要選另一個y,也就是綁定的話,建從x到y的容量為INF的邊,然後從S向所有正值x的點建容量為x的邊,從所有負值x向T建容量為|x|的邊,然後跑從S到T的最小割(最大流),用原序列的所有正值的和sum來減去最小割,得到的就是最大權閉合子圖的權值,也就是結果。
本題的思想轉化為,如果要保留編號為i個數,就必須保留所有編號為i的因子的數,也就是對第i個數,找出所有存在的編號為i的整數倍k*i的數,建從k*i到i的INF的邊。
以下是AC代碼:
1 #include<bits/stdc++.h>
2 using namespace std;
3 const int N=2e3;
4 const int INF=0x3f3f3f3f;
5 int n;
6 struct Dinic
7 {
8 struct Edge
9 {
10 int from, to, cap, flow;
11 Edge(int u, int v, int c, int f)
12 : from(u), to(v), cap(c), flow(f) {}
13 };
14 int n, m, s, t;
15 vector<Edge> edges;
16 vector<int> G[N];
17 bool vis[N];
18 int d[N];
19 int cur[N];
20 void init(int n)
21 {
22 this->n = n;
23 for (int i = 0; i < n; i++) G[i].clear();
24 edges.clear();
25 }
26 void AddEdge(int from, int to, int cap)
27 {
28 edges.emplace_back(from, to, cap, 0);
29 edges.emplace_back(to, from, 0, 0);
30 m = edges.size();
31 G[from].push_back(m - 2);
32 G[to].push_back(m - 1);
33 }
34 bool BFS()
35 {
36 memset(vis, 0, sizeof(vis));
37 memset(d, 0, sizeof(d));
38 queue<int> q;
39 q.push(s);
40 d[s] = 0;
41 vis[s] = 1;
42 while (!q.empty())
43 {
44 int x = q.front();
45 q.pop();
46 for (int i = 0; i < G[x].size(); i++)
47 {
48 Edge& e = edges[G[x][i]];
49 if (!vis[e.to] && e.cap > e.flow)
50 {
51 vis[e.to] = 1;
52 d[e.to] = d[x] + 1;
53 q.push(e.to);
54 }
55 }
56 }
57 return vis[t];
58 }
59 int DFS(int x, int a)
60 {
61 if (x == t || a == 0) return a;
62 int flow = 0, f;
63 for (int& i = cur[x]; i < G[x].size(); i++)
64 {
65 Edge& e = edges[G[x][i]];
66 if (d[x] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap - e.flow))) > 0)
67 {
68 e.flow += f;
69 edges[G[x][i] ^ 1].flow -= f;
70 flow += f;
71 a -= f;
72 if (a == 0) break;
73 }
74 }
75 return flow;
76 }
77 int Maxflow(int s, int t)
78 {
79 this->s = s, this->t = t;
80 int flow = 0;
81 while (BFS())
82 {
83 memset(cur, 0, sizeof(cur));
84 flow += DFS(s, INF);
85 }
86 return flow;
87 }
88 } solver;
89
90 int main()
91 {
92 scanf("%d",&n);
93 solver.init(n+3);
94 int s=0,t=n+1,x,sum=0;
95 for(int i=1;i<=n;++i)
96 {
97 scanf("%d",&x);
98 if(x<0)
99 solver.AddEdge(i,t,-x);
100 else
101 {
102 solver.AddEdge(s,i,x);
103 sum+=x;
104 }
105 for(int j=2;i*j<=n;++j)
106 solver.AddEdge(i*j,i,INF);
107 }
108 printf("%d\n",sum-solver.Maxflow(s,t));
109 }