Loading

P5360 [SDOI2019] 世界地图

超级好的题!

首先考虑一下 L=2 的情况,它可以等价于只查询一个后缀,这引导我们想一个动态加点加边求最小生成树的做法,发现这是 LCT 基础练习题,如果要加入 (u,v) 这条边,如果不连通直接加,否则删除换上最大的边就行了。

而原问题需要我们合并前后缀,发现合并复杂度爆了。发现我们实际上并没有利用到网格图的性质,当我们在处理后缀的时候,我们断的边不可能是一条连着的点都不是最左边一列中的点的边,这启发我们动态维护最左边一列的点建的虚树,这样子复杂度就可以控制在 O(nmlogn) 了。

那么我们对于前后缀分别做一次每次查询合并就好了,时间复杂度 O(nmlogn)

代码:

#include <bits/stdc++.h>
#define rep(i, l, r) for (int i (l); i <= (r); ++ i)
#define rrp(i, l, r) for (int i (r); i >= (l); -- i)
#define eb emplace_back
#define pii pair <int, int>
#define inf 1000000000
#define ls (p << 1)
#define rs (ls | 1)
using namespace std;
constexpr int N = 1e4 + 5, M = 105, P = 1e9 + 7;
typedef long long ll;
typedef unsigned long long ull;
inline int rd () {
  int x = 0, f = 1;
  char ch = getchar ();
  while (! isdigit (ch)) {
    if (ch == '-') f = -1;
    ch = getchar ();
  }
  while (isdigit (ch)) {
    x = (x << 1) + (x << 3) + (ch ^ 48);
    ch = getchar ();
  }
  return x * f;
}
int qpow (int x, int y, int p = P) {
  int ret = 1;
  for (; y; y >>= 1, x = x * x % p) if (y & 1) ret = ret * x % p;
  return ret;
}
int n, m, lim;
unsigned int SA, SB, SC;
inline int read() 
{
	SA ^= SA << 16;
	SA ^= SA >> 5;
	SA ^= SA << 1;
	ll t = SA;
	SA = SB;
	SB = SC;
	SC ^= t ^ SA;
	return SC % lim + 1;
} 
int a[N][M], b[N][M];
int is[N * M];
class EDG {
	public:
		int u, v, w;
		friend bool operator < (const EDG &a, const EDG &b) {
			return a.w < b.w;
		}
};
class MST {
	public:
		vector <EDG> e;
		int tot; ll sum;
		void init (int * a) {
			tot = n; sum = 0;
			rep (i, 1, n - 1) e.eb ((EDG) {i, i + 1, a[i]});
		}
		ll qry () {
			ll ret (sum);
			for (auto p : e) ret += p.w;
			return ret;
		}
} pre[N], suf[N];
int fa[N];
vector <pii> g[N];
int find (int x) {
	return x == fa[x] ? x : fa[x] = find (fa[x]);
}
bool dfs (int u, int fa) {
	int s (0);
	for (auto p : g[u]) if (p.first ^ fa) s += dfs (p.first, u);
	is[u] |= s >= 2;
	return s | is[u];
}
vector <EDG> e;
ll sum;
void dfs (int u, int fa, int zu, int mx) {
	if (is[u]) {
		if (zu) e.eb ((EDG) {is[u], zu, mx});
		sum -= mx, mx = 0; zu = is[u];
	}
	for (auto p : g[u]) if (p.first ^ fa) dfs (p.first, u, zu, max (mx, p.second));
}
MST merge (MST a, MST b, int * c) {
	MST ret;
	e.clear ();
	for (auto p : a.e) e.eb (p);
	for (auto p : b.e) e.eb ((EDG) {p.u + a.tot, p.v + a.tot, p.w});
	rep (i, 1, n) e.eb ((EDG) {a.tot + i - n, a.tot + i, c[i]});
	sort (e.begin (), e.end ());
	rep (i, 1, a.tot + b.tot) {
		fa[i] = i;
		is[i] = (i <= n || i > a.tot + b.tot - n);
		g[i].clear ();
	}
	sum = 0;
	for (auto p : e) {
		int u (p.u), v (p.v), w (p.w);
		if (find (u) ^ find (v)) {
			g[u].eb (pii (v, w)), g[v].eb (pii (u, w));
			fa[find (u)] = v; sum += w;
		}
	} dfs (1, 0);
	int cnt (0);
	rep (i, 1, a.tot + b.tot) {
		if (is[i]) is[i] = ++ cnt;
	} e.clear ();
	dfs (1, 0, 0, 0);
	ret.tot = cnt; ret.sum = a.sum + b.sum + sum, ret.e = e;
	return ret;
}
int32_t main () {
  // freopen ("1.in", "r", stdin);
  // freopen ("1.out", "w", stdout);
  scanf ("%d%d%u%u%u%d", &n, &m, &SA, &SB, &SC, &lim);
  rep (i, 1, n) rep (j, 1, m) a[j][i] = read ();
  rep (i, 1, n - 1) rep (j, 1, m) b[j][i] = read ();
  rep (i, 1, m) pre[i].init (b[i]), suf[i].init (b[i]);
  rep (i, 2, m - 1) pre[i] = merge (pre[i - 1], pre[i], a[i - 1]);
  rrp (i, 2, m - 1) suf[i] = merge (suf[i], suf[i + 1], a[i]);
  for (int q (rd ()); q; -- q) {
  	int L (rd ()), R (rd ());
  	printf ("%lld\n", merge (suf[R + 1], pre[L - 1], a[m]).qry ());
	}
}

作者:lalaouye

出处:https://www.cnblogs.com/lalaouyehome/p/18739538

版权:本作品采用「114514」许可协议进行许可。

posted @   lalaouye  阅读(1)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示