AtCoder Regular Contest 115 F Migration

洛谷传送门

AtCoder 传送门

这种最大值最小化的题一般可以先考虑二分。设二分了一个 \(mid\)

\(A = (a_1,a_2,...,a_k)\) 为表示每个棋子的位置的状态,如果 \(A,B\) 可以互相到达,就在它们之间连一条无向边。则要判断的是 \(S = (s_1,s_2,...,s_k)\)\(T = (t_1,t_2,...,t_k)\) 是否在同一连通块内。

\(f(A)\)\(\sum\limits_{i=1}^k a_{A_i}\),那我们不妨让 \(S\)\(T\) 都到达一个 \(f(A)\) 最小的状态 \(A\)(如果 \(f(A)\) 相同则比较字典序)。如果两个 \(A\) 都相同,则 \(S,T\) 在同一连通块内,否则不在。

\(g(A)\)\(A\) 能到达的最小状态,则问题转化成了求 \(g(A)\)

考虑一个暴力做法:每次找到能移的棋子中造成的贡献最大的,移动这个棋子(需要预处理点 \(u\) 经过权值不超过 \(D\) 的点能到达的所有点的点权最小值)。因为每个棋子的移动不会重复经过某个点,所以最多移动 \(O(nk)\) 次。时间复杂度是 \(O(nk^2 \log n \log ans)\)显然会 T

考虑加点优化。用堆维护下一个贡献最大的操作。每次 \(\sum\limits_{i=1}^k a_i\) 变小时,一些点的移动会变得合法,将它们加入堆中即可。需要精细实现,复杂度 \(O(\text{可过})\)

code
// Problem: F - Migration
// Contest: AtCoder - AtCoder Regular Contest 115
// URL: https://atcoder.jp/contests/arc115/tasks/arc115_f
// Memory Limit: 1024 MB
// Time Limit: 4000 ms
// 
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

namespace IO {
    char ibuf[(1 << 20) + 1], *iS, *iT;
#if ONLINE_JUDGE
#define gh() (iS == iT ? iT = (iS = ibuf) + fread(ibuf, 1, (1 << 20) + 1, stdin), (iS == iT ? EOF : *iS++) : *iS++)
#else
#define gh() getchar()
#endif
    inline long long read() {
        char ch = gh();
        long long x = 0;
        bool t = 0;
        while (ch < '0' || ch > '9') t |= ch == '-', ch = gh();
        while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (ch ^ 48), ch = gh();
        return t ? ~(x - 1) : x;
    }
    template <typename _Tp>
    inline void write(_Tp x) {
    	static char stk[64], *top = stk;
        if (x < 0) {
            x = ~(x - 1);
            putchar('-');
        }
        do *top++ = x % 10, x /= 10;
        while (x);
        while (top != stk) putchar((*--top) | 48);  
    }
}

using IO::read;
using IO::write;

const int maxn = 2020;
const ll inf = 0x3f3f3f3f3f3f3f3fLL;

ll n, m, a[maxn], head[maxn], len, lsh[maxn], tot;
int c[maxn], d[maxn], p[maxn], e[maxn], to[maxn << 1], nxt[maxn << 1];
pii f[maxn][maxn];
bool vis[maxn];
int stk[maxn];

struct piece {
	ll s, t;
} b[maxn];

inline void add_edge(int u, int v) {
	to[++len] = v;
	nxt[len] = head[u];
	head[u] = len;
}

struct node {
	int u;
	ll val;
	int t, i, j;
} h[maxn];

unordered_set<int> st[maxn];

inline bool operator < (const node &a, const node &b) {
	if (a.val != b.val) {
		return a.val < b.val;
	}
	bool x = (a.t < a.u), y = (b.t < b.u);
	if (x && y) {
		return a.i > b.i;
	} else if (!x && !y) {
		return a.i < b.i;
	} else {
		return !x;
	}
}

struct wwh {
	ll x;
	int y, z;
} g[maxn * maxn];

inline bool operator < (const wwh &a, const wwh &b) {
	return a.x > b.x;
}

namespace SGT {
	node tree[maxn << 2];
	
	void build(int rt, int l, int r) {
		if (l == r) {
			tree[rt] = h[c[l]];
			tree[rt].i = l;
			return;
		}
		int mid = (l + r) >> 1;
		build(rt << 1, l, mid);
		build(rt << 1 | 1, mid + 1, r);
		tree[rt] = max(tree[rt << 1], tree[rt << 1 | 1]);
	}
	
	void update(int rt, int l, int r, int x, node &y) {
		if (l == r) {
			tree[rt] = y;
			tree[rt].i = l;
			return;
		}
		int mid = (l + r) >> 1;
		(x <= mid) ? update(rt << 1, l, mid, x, y) : update(rt << 1 | 1, mid + 1, r, x, y);
		tree[rt] = max(tree[rt << 1], tree[rt << 1 | 1]);
	}
}

inline void work(ll x) {
	ll s = 0;
	for (int i = 1; i <= n; ++i) {
		st[i].clear();
	}
	for (int i = 1; i <= m; ++i) {
		s += a[c[i]];
		st[c[i]].insert(i);
	}
	int tt = 0;
	for (int i = 1; i <= n; ++i) {
		node mx = (node){i, -1, 0, i, -1};
		for (int j = 1; j <= tot; ++j) {
			if (f[i][j].scd == -1) {
				continue;
			}
			node t = (node){i, a[i] - f[i][j].fst, f[i][j].scd, i, j};
			if (lsh[j] + s - a[i] <= x) {
				p[i] = j;
				mx = max(mx, t);
			} else if (x + a[i] - lsh[j] > 0 && mx < t) {
				g[++tt] = (wwh){x + a[i] - lsh[j], i, j};
			}
		}
		h[i] = mx;
	}
	sort(g + 1, g + tt + 1);
	SGT::build(1, 1, m);
	int pos = 1;
	while (1) {
		node u = SGT::tree[1];
		if (u.val < 0 || (u.val == 0 && c[u.i] <= u.t)) {
			break;
		}
		s -= u.val;
		st[u.u].erase(u.i);
		c[u.i] = u.t;
		st[u.t].insert(u.i);
		SGT::update(1, 1, m, u.i, h[u.t]);
		int top = 0;
		while (pos <= tt && s <= g[pos].x) {
			int i = g[pos].y, j = g[pos].z;
			p[i] = j;
			node t = (node){i, a[i] - f[i][j].fst, f[i][j].scd, i, j};
			if (h[i] < t) {
				h[i] = t;
				if (!vis[i]) {
					vis[i] = 1;
					stk[++top] = i;
				}
			}
			++pos;
		}
		for (int _ = 1; _ <= top; ++_) {
			int i = stk[_];
			vis[i] = 0;
			for (int x : st[i]) {
				 SGT::update(1, 1, m, x, h[i]);
			}
		}
	}
}

inline bool check(ll x) {
	for (int i = 1; i <= m; ++i) {
		c[i] = b[i].s;
	}
	work(x);
	for (int i = 1; i <= m; ++i) {
		d[i] = c[i];
		c[i] = b[i].t;
	}
	work(x);
	for (int i = 1; i <= m; ++i) {
		if (c[i] != d[i]) {
			return 0;
		}
	}
	return 1;
}

void dfs(int u, int fa, int mx, int rt) {
	f[rt][mx] = min(f[rt][mx], make_pair(a[u], 1LL * u));
	for (int i = head[u]; i; i = nxt[i]) {
		int v = to[i];
		if (v == fa) {
			continue;
		}
		dfs(v, u, max(mx, e[v]), rt);
	}
}

void solve() {
	n = read();
	for (int i = 1; i <= n; ++i) {
		a[i] = read();
		lsh[++tot] = a[i];
	}
	sort(lsh + 1, lsh + tot + 1);
	tot = unique(lsh + 1, lsh + tot + 1) - lsh - 1;
	for (int i = 1; i <= n; ++i) {
		e[i] = lower_bound(lsh + 1, lsh + tot + 1, a[i]) - lsh;
	}
	for (int i = 1, u, v; i < n; ++i) {
		u = read();
		v = read();
		add_edge(u, v);
		add_edge(v, u);
	}
	m = read();
	ll s1 = 0, s2 = 0;
	for (int i = 1; i <= m; ++i) {
		b[i].s = read();
		b[i].t = read();
		s1 += a[b[i].s];
		s2 += a[b[i].t];
	}
	for (int u = 1; u <= n; ++u) {
		for (int i = 0; i <= tot; ++i) {
			f[u][i] = make_pair(inf, -1);
		}
		dfs(u, -1, e[u], u);
		for (int i = 1; i <= tot; ++i) {
			f[u][i] = min(f[u][i], f[u][i - 1]);
		}
	}
	ll l = max(s1, s2), r = *max_element(a + 1, a + n + 1) * m, ans = -1;
	while (l <= r) {
		ll mid = (l + r) >> 1;
		if (check(mid)) {
			ans = mid;
			r = mid - 1;
		} else {
			l = mid + 1;
		}
	}
	write(ans);
}

int main() {
	solve();
	return 0;
}
posted @ 2023-04-25 13:02  zltzlt  阅读(11)  评论(0编辑  收藏  举报