CF818G Four Melodies
题目大意:
给出一个长度为 \(n\) 的数列,我们要尽量找出 \(4\) 个总长最大的互不相交的子序列,每个子序列的相邻两个元素要满足下面条件的其中之一:
- \(|x-y|=1\)
- \(x\equiv y\pmod 7\)
解法:
这题乍一看是明显会被误以为是 dp。但是这题需要我们总长最大,dp 的话很难做到的,所以我们尝试转化为图论建模。仔细想想发现最长路应该不对,考虑网络最小费用最大流。
由于作者不喜欢暴力的 \(O(n^2)\) 直接建边。于是我们考虑将每个下标位置进行拆点,\(in_i\),\(out_i\),\(m_i\),\(idt_i\),分别表示入点、出点、同余点和绝对值点。我们还要多出一个点 \(S_{lim}\),对最多四个子序列进行限制,即 $S\to S_{lim} $ 的流是 \(4\),费用为 \(0\)。剩下诸如此类考虑,可以得到下面式子:
- \(S_{lim} \to in_i\) 的流是 \(1\),费用为 \(0\)。
- \(in_i \to out_i\) 的流是 \(1\),费用为 \(-1\)。(选择 \(i\),得到 \(1\) 的价值)
- \(out_i \to m_{lstm_i}\)、\(idt_{lsta_i}\) 的流是 \(1\),费用为 \(0\)。\(lstm_i\) 和 \(lsta_i\) 分别是上一个和自己同余的和上一个和自己相差 \(1\) 的。(达成条件的加入子序列)
- \(idt_i\to idt_{lsti_i}\) 的流是 \(4\),费用为 \(0\)。\(lsti_i\) 是上一个与 \(i\) 相同的下标。(相同的构成一条链)
- \(m_i\to m_{lstm_i}\) 的流是 \(4\),费用为 \(0\)。(同余的构成一条链)
然后跑一下 MCMF 就好。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 15010, M = 3010, inf = 0x3f3f3f3f;
struct node {
int lst, v, w, c;
} e[N << 3];
int a[M], lstm[7], lsti[100010], nxtm[M], nxta1[M], nxta2[M], nxti[M];
int n, S, T, tot = -1;
int h[N], in[N], pre[N], d[N], cur[N];
bool vis[N];
ll ans, cost;
void add(int u, int v, int w, int c){
e[++tot] = {h[u], v, w, c}; h[u] = tot;
e[++tot] = {h[v], u, 0, -c}; h[v] = tot;
}
bool spfa() {
memset(vis, 0, sizeof vis);
memset(d, 0x3f, sizeof d);
queue<int> q;
q.push(S); vis[S] = 1; d[S] = 0; in[S] = inf;
while(!q.empty()) {
int u = q.front(); q.pop();
vis[u] = 0;
for(int i = h[u]; ~i; i = e[i].lst) {
int v = e[i].v, w = e[i].w, c = e[i].c;
if(d[v] > d[u] + c && w > 0) {
d[v] = d[u] + c;
if(!vis[v]) q.push(v), vis[v] = 1;
}
}
}
return d[T] != inf;
}
int dfs(int u, int in) {
if(u == T || !in) return in;
int out = 0; vis[u] = 1;
for(int &i = cur[u]; ~i; i = e[i].lst) {
int v = e[i].v, w = e[i].w, c = e[i].c;
if(!vis[v] && w > 0 && d[v] == d[u] + c) {
int f = dfs(v, min(in, w));
if(f == 0) continue;
e[i].w -= f; e[i ^ 1].w += f;
in -= f; out += f; cost += f * c;
if(!in) break;
}
}
vis[u] = 0;
if(!out) d[u] = -1;
return out;
}
void MCMF() {
while(spfa()) {
memcpy(cur, h, sizeof h);
int f = 0;
while((f = dfs(S, inf)) > 0) ans += f;
}
}
int main(){
memset(h, -1, sizeof h);
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
for(int i = n; i; i--) {
nxtm[i] = lstm[a[i] % 7];
nxti[i] = lsti[a[i]];
nxta1[i] = lsti[a[i] + 1];
nxta2[i] = lsti[a[i] - 1];
lsti[a[i]] = lstm[a[i] % 7] = i;
}
S = 0; T = 4 * n + 1;
int S_lim = 4 * n + 2;
add(S, S_lim, 4, 0);
for(int i = 1; i <= n; i++) {
add(S_lim, i, 1, 0);
add(i, n + i, 1, -1);
add(n + i, T, 1, 0);
add(2 * n + i, i, 1, 0);
add(3 * n + i, i, 1, 0);
if(nxtm[i]) {
add(n + i, 2 * n + nxtm[i], 1, 0);
add(2 * n + i, 2 * n + nxtm[i], 4, 0);
}
if(nxti[i]) add(3 * n + i, 3 * n + nxti[i], 4, 0);
if(nxta1[i]) add(n + i, 3 * n + nxta1[i], 1, 0);
if(nxta2[i]) add(n + i, 3 * n + nxta2[i], 1, 0);
}
MCMF();
printf("%d\n", -cost);
return 0;
}

浙公网安备 33010602011771号