Rainbow Bracket Sequence

qjbqjb發表於2024-09-18

The 2024 ICPC Asia East Continent Online Contest (I)

題意

構造長度為 \(2n\) 的合法括號序列。

對於每個左括號在的位置 \(i\), 都有顏色 \(c_i\) 和價值 \(v_i\)

左括號顏色視為所在位置顏色, 價值同理。

對於每個顏色,滿足左括號為該顏色的個數 \(\geq l_i\)

求滿足以上條件下,最大左括號的價值和。

\(n \leq 100, m \leq n, v_i \leq 10^9\)

做法

是費用流,關鍵在於建模。

  • 保證合法括號序列

首先構建點 \(1 \dots 2n\) 表示所有括號序列。

把原點 \(S\) 連向所有奇數點,即 \(S \to 1, S\to 3, \dots, S \to 2n - 1\)

流量,邊權 為 \((1, 0)\), 即表示左括號在這些點。

同時,對 \(i + 1 \to i\), 連 \((+\infty, 0)\), 表示左括號可以向左移動。

可以發現, 這樣構造出來的括號序列總是合法的, 總能保證左右括號匹配且任意字首左括號個數大於等於右括號個數。

  • 顏色限制

首先, 每個位置 \(i\) 連向對應顏色 \(c_i\), 邊權為 \(-v_i\), 流量為 \(1\)

為了保證顏色 \(i\)\(l_i\) 個, 連 \(c_i \to T\) 時, 流量為 \(l_i\), 邊權為 \(-\infty\),(可以是極小的數 \(-10^{13}\)) 表示一定要選。

對於超過 \(l_i\) 的部分,連 \(c_i \to T\)\((\infty, 0)\) 的邊, 表示可以選。

  • 跑最小費用最大流即可

求得 \(\lfloor\frac{\text{mincost}}{-10^{13}}\rfloor = \sum l_i\), 即有解,

最後的答案為 \(\text{mincost} \mod (-10^{13})\)

code

#include<bits/stdc++.h>
using namespace std;
using ll = long long;  
const int N = 10100;
const ll INF = 1e13; 

int n, m, s, t;
int col[N], val[N], l[N]; 

struct edge {
	int v, len, next;
	ll w;  
} e[N];
int cnt;
int first[N], cur[N]; 

void add(int u, int v, int len, ll w) {
	++ cnt;
	e[cnt].v = v;
	e[cnt].len = len; 
	e[cnt].w = w; 
	e[cnt].next = first[u];
	first[u] = cnt; 
}
void Add(int u, int v, int len, ll w) {
	add(u, v, len, w); 
	add(v, u, 0, -w); 
} 
void init() {
	cnt = 1; 
	s = 2 * n + m + 1, t = 2 * n + m + 2; 
	for (int i = 1; i <= t; i ++)
		first[i] = 0;   
}

bool vis[N];
ll dis[N];  
bool bfs() {
	memcpy(cur, first, sizeof(first)); 
	for (int i = 1; i <= t; i ++)
		vis[i] = 0, dis[i] = INF;
	queue<int> q; 
	q.push(s); 
	dis[s] = 0; 
	while (!q.empty()) {
		int u = q.front(); q.pop();
		vis[u] = 0; 
		for (int i = first[u]; i; i = e[i].next) {
			int v = e[i].v, len = e[i].len;
			ll w = e[i].w; 
			if (!len || dis[v] <= dis[u] + w) continue;
			dis[v] = dis[u] + w;
			if (!vis[v]) {
				q.push(v),
				vis[v] = 1; 
			}
		}
	}
	return dis[t] != INF; 
}
ll cost; 
int dfs(int u, int flow) {
	if (u == t) {
		return flow; 
	}
	int ans = 0; 
	vis[u] = 1;
	for (int &i = cur[u]; i; i = e[i].next) {
		int v = e[i].v, len = e[i].len; ll w = e[i].w; 
		if (vis[v] || dis[v] != dis[u] + w || !len) continue; 
		int out = dfs(v, min(len, flow));
		if (out) {
			ans += out; 
			cost += 1ll * out * w; 
			e[i].len -= out;
			e[i ^ 1].len += out; 
			flow -= out; 
		} 
	}
	return ans; 
}
ll dinic() {
	ll ans = 0; 
	while (bfs()) {
		cost = 0; 
		dfs(s, 0x7fffffff);
		ans += cost;
	}
	return ans; 
}

void solve() {
	cin >> n >> m; 
	init(); 
	int suml = 0; 
	for (int i = 1; i <= m; i ++)
		cin >> l[i], suml += l[i];
	for (int i = 1; i <= 2 * n; i ++)	
		cin >> col[i]; 
	for (int i = 1; i <= 2 * n; i ++)
		cin >> val[i]; 
		
	for (int i = 1; i <= 2 * n; i += 2)
		Add(s, i, 1, 0); 
	for (int i = 1; i <= 2 * n - 1; i ++) 
		Add(i + 1, i, 0x7fffffff, 0); 
	for (int i = 1; i <= 2 * n; i ++)
		Add(i, 2 * n + col[i], 1, - val[i]); 
	for (int i = 1; i <= m; i ++) {
		Add(2 * n + i, t, l[i], -INF);
		Add(2 * n + i, t, 0x7fffffff, 0); 
	}
	ll cost = dinic();
	if (cost / (-INF) == suml) {
		cout << (-cost) % INF << endl;
	} else
		cout << -1 << endl;
} 

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0); 
	int T;
	cin >>T;
	while (T --)
		solve(); 
	return 0;
}

最後 %%%klii , 感謝他的圖和教導。

相關文章