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;
}
posted @ 2026-07-27 16:31  OIerYang  阅读(9)  评论(0)    收藏  举报