240104 杂题全谈 四边形不等式

因为输入法没有给我满意的候选项所以这次就不取抽象标题了。


A - Chef and Bitwise OR Operation

https://vjudge.net/contest/602275#problem/A

CodeChef - CHEFAOR。

看到「刚好 \(K\) 个」,脑子一热差点去写 wqs(

实际上当然是不需要的。观察数据范围,至少 \(n^2\) 跑得过。令 \(f_{i, j}\) 表示将前 \(i\) 个分为 \(j\) 段的最大价值。预处理一个 \(s_{l,r}\) 表示 \([l,r]\) 按位或起来的值,用一个类前缀和就可以 \(\mathcal O(n^2)\)。那么有:

\[f_{i,j}=\max\{f_{k,j-1}+s_{k+1,i}\} \]

然后对于代价函数 \(w=s\),四边形不等式成立。不是很想写,但是还是写一下吧。

就,最好还是把线段图画出来,其实挺显然的。

假设 \(x=w(b,d),y=w(a,d)-w(b,d),z=w(a,c)-y\),有 \(w(a,c)=y+z,w(a,c)+w(b,d)=x+y+z\),这是交叉。

那么 \(w(b,c)\ge z,w(a,d)=x+y\),有 \(w(a,d)+w(b,c)\ge x+y+z\),这是包含。交叉小于包含,四边形不等式成立。

那么就可以直接写了。复杂度 \(\mathcal O(T\times n^2)\)

#define int long long
namespace XSC062 {
using namespace fastIO;
const int maxn = 5e3 + 5;
int T, n, k;
int a[maxn];
int s[maxn][maxn];
int f[maxn][maxn], w[maxn][maxn];
int main() {
	read(T);
	while (T--) {
		read(n), read(k);
		for (int i = 1; i <= n; ++i) read(a[i]);
		memset(w, 0, sizeof (w));
		memset(f, -0x3f, sizeof (f));
		for (int i = 1; i <= n; ++i) {
			for (int j = i; j <= n; ++j) {
				s[i][j] = s[i][j - 1] | a[j];
//				printf("s[%lld][%lld] = %lld\n",
//							i, j, s[i][j]);
			}
			f[i][1] = s[1][i];
			w[i][1] = 0;
		}
		for (int i = 1; i <= n; ++i) {
			for (int j = 2;
						j <= i && j <= k; ++j) {
				for (int k = i - 1;
					k >= w[i - 1][j - 1]; --k) {
					if (f[i][j] < f[k][j - 1]
								+ s[k + 1][i]) {
						f[i][j] = f[k][j - 1]
								+ s[k + 1][i];
						w[i][j] = k;
					}
				}
//				printf("f[%lld][%lld] = %lld\n",
//						i, j, f[i][j]);
			}
		}
		print(f[n][k], '\n');
	}
	return 0;
}
} // namespace XSC062
#undef int

B - Lawrence

https://vjudge.net/contest/602275#problem/B

HDU2829。

大意就是把一个长度为 \(n\) 的序列分成连续的 \(m+1\) 段,定义每段的价值为任意两个数相乘的和。

虽然但是,好板。然后我们看看每段的价值怎么算。数学一下就是 \(\dfrac {(\sum a_i)^2-\sum {a_i}^2}2\)

然后证明显然吧,大概。所以直接写。

#define int long long
namespace XSC062 {
using namespace fastIO;
const int maxn = 1e3 + 5;
int n, m;
int a[maxn];
int s[maxn][maxn];
int f[maxn][maxn], w[maxn][maxn];
int main() {
	read(n), read(m);
	while (m++ || n) {
		for (int i = 1; i <= n; ++i) read(a[i]);
		memset(w, 0, sizeof (w));
		memset(f, 0x3f, sizeof (f));
		for (int i = 1; i <= n; ++i) {
			int s1 = 0, s2 = 0;
			for (int j = i; j <= n; ++j) {
				s1 += a[j], s2 += a[j] * a[j];
				s[i][j] = (s1 * s1 - s2) / 2;
			}
			f[i][1] = s[1][i];
			w[i][1] = 0;
		}
		for (int i = 1; i <= n; ++i) {
			for (int j = 2;
						j <= i && j <= m; ++j) {
				for (int k = i - 1;
					k >= w[i - 1][j - 1]; --k) {
					if (f[i][j] > f[k][j - 1]
								+ s[k + 1][i]) {
						f[i][j] = f[k][j - 1]
								+ s[k + 1][i];
						w[i][j] = k;
					}
				}
//				printf("f[%lld][%lld] = %lld\n",
//						i, j, f[i][j]);
			}
		}
		print(f[n][m], '\n');
		read(n), read(m);
	}
	return 0;
}
} // namespace XSC062
#undef int

C - Monkey Party

https://vjudge.net/contest/602275#problem/C

HDU3506。

环形石子合并。不妨先回忆一下线性的怎么做,我们打了个区间 DP,式子是 \(f_{i,j}=\min\{f_{i,k}+f_{k+1,j}+s_{i,j}\}\)

环形的很好整,我们复制一遍序列然后在所有长度为 \(n\) 的区间中找最小的 \(f\) 就好了。

然后套一个四边形不等式就可以了。

需要注意的是本题的 \(n\) 可能等于 \(1\),所以可能会有一些奇怪的细节导致挂分 😭

namespace XSC062 {
const int maxn = 2e3 + 5;
const int inf = 0x3f3f3f3f;
int n, res;
int a[maxn], s[maxn];
int f[maxn][maxn], w[maxn][maxn];
int min(int x, int y) {
	return x < y ? x : y;
}
int main() {
	while (~scanf("%d", &n)) {
		memset(w, 0, sizeof (w));
		memset(f, 0x3f, sizeof (f));
		for (int i = 1; i <= n; ++i) {
			scanf("%d", &a[i]);
			a[n + i] = a[i];
			s[i] = s[i - 1] + a[i];
		}
		for (int i = n + 1; i <= 2 * n; ++i)
			s[i] = s[i - 1] + a[i];
		for (int i = 1; i <= 2 * n; ++i)
			f[i][i] = 0, w[i][i] = i;
		for (int l = 2; l <= n; ++l) {
			for (int i = 1;
					i <= 2 * n - l + 1; ++i) {
				int j = i + l - 1;
				for (int k = w[i][j - 1];
						k <= w[i + 1][j]
						&& k < j; ++k) {
					int val = f[i][k] +
								f[k + 1][j] +
								s[j] - s[i - 1];
					if (val < f[i][j]) {
						f[i][j] = val;
						w[i][j] = k;
					}
				}
			}
		}
		res = inf;
		for (int i = 1; i <= n; ++i)
			res = min(res, f[i][i + n - 1]);
		printf("%d\n", res);
	}
	return 0;
}
} // namespace XSC062

D - Division

https://vjudge.net/contest/602275#problem/D

HDU3480。

这个主要问题在 \(n\)\(m\) 都特别大,时间特别卡。排序是不难想的,只要你不和我一样排完序还要线性求区间极差。

然后这个四边形要好好写,不然会 T 飞。

namespace XSC062 {
const int maxn = 1e4 + 5;
const int maxm = 5e3 + 5;
const int inf = 0x3f3f3f3f;
int a[maxn];
int T, n, m, _t;
int f[maxm][maxn], w[maxm][maxn];
int min(int x, int y) {
	return x < y ? x : y;
}
int max(int x, int y) {
	return x > y ? x : y;
}
int main() {
	scanf("%d", &T);
	while (T--) {
		scanf("%d %d", &n, &m);
		for (int i = 1; i <= n; ++i)
			scanf("%d", &a[i]);
		std::sort(a + 1, a + n + 1);
		for (int i = 1; i <= n; ++i) {
			f[1][i] = (a[i] - a[1]) * (a[i] - a[1]);
			w[1][i] = 0;
		}
		for (int i = 2; i <= m; ++i) {
			w[i][n + 1] = n;
			for (int j = n; j > i; --j) {
				f[i][j] = inf;
				for (int k = w[i - 1][j];
						k <= w[i][j + 1]; ++k) {
					if (f[i][j] > f[i - 1][k]
							+ (a[j] - a[k + 1])
							* (a[j] - a[k + 1])) {
						f[i][j] = f[i - 1][k]
							+ (a[j] - a[k + 1])
							* (a[j] - a[k + 1]);
						w[i][j] = k;
					}
				}
			}
		}
		printf("Case %d: %d\n", ++_t, f[m][n]);
	}
	return 0;
}
} // namespace XSC062

E - Ciel and Gondolas

https://vjudge.net/contest/602275#problem/E

我觉得我算是发现了四边形不等式板子的规律。

长得差不多的式子,要么区间 DP 要么连续划分,然后加一些挂件。

比如这道题我们可以发现将 \([l,r]\) 划分成一段的代价是以 \((l, l)\) 为左上角,\((r,r)\) 为右下角的二维前缀和。

然后就跟前面一样了。式子都不用变的。

namespace XSC062 {
using namespace fastIO;
const int maxm = 805;
const int maxn = 4e3 + 5;
const int inf = 0x3f3f3f3f;
int n, m;
int a[maxn][maxn];
int f[maxm][maxn], w[maxm][maxn];
int min(int x, int y) {
	return x < y ? x : y;
}
int max(int x, int y) {
	return x > y ? x : y;
}
int sum(int i, int j) {
	return (a[j][j] - a[i - 1][j] -
		a[j][i - 1] + a[i - 1][i - 1]) / 2;
}
int main() {
	read(n), read(m);
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= n; ++j) {
			read(a[i][j]);
			a[i][j] += a[i - 1][j] +
				a[i][j - 1] - a[i - 1][j - 1];
		}
	}
	for (int i = 1; i <= n; ++i) {
		f[1][i] = sum(1, i);
		w[1][i] = 0;
	}
	for (int i = 2; i <= m; ++i) {
		w[i][n + 1] = n;
		for (int j = n; j > i; --j) {
			f[i][j] = inf;
			for (int k = w[i - 1][j];
					k <= w[i][j + 1]; ++k) {
				if (f[i][j] > f[i - 1][k]
						+ sum(k + 1, j)) {
					f[i][j] = f[i - 1][k]
						+ sum(k + 1, j);
					w[i][j] = k;
				}
			}
		}
	}
	print(f[m][n], '\n');
	return 0;
}
} // namespace XSC062
posted @ 2024-01-04 15:51  XSC062  阅读(30)  评论(0编辑  收藏  举报