其他分享
首页 > 其他分享> > JZOJ3166. 火星菌

JZOJ3166. 火星菌

作者:互联网

题目大意

一棵\(n\)层线段树, 可以交换任何一个点的左子树和右子树, 使得叶子节点的顺序改变. 两个叶子节点相邻会有\(w_{i,j}\)的代价, 求最小代价.
\(n<=9\)

解题思路

不显然的是个dp题呢.
设\(f_{i,j}\)表示在第\(i\)个点放\(j\)的代价. 枚举前一个叶子节点放的数\(k\)考虑转移. 那么问题就出在\(k\)的范围.

考虑如果我们把\(i - 1\)作为线段树区间的\(mid\), \(i\)作为线段树区间的\(mid + 1\), 那么能预处理出所对应的线段树区间\([l,r]\). 由此可以根据\(j\)算出\(k\)的范围.

手推一下还是很好理解的. 时间复杂度看起来是\(O(2^{3n})\)的, 不过据说是\(O(2^{2n}\log(2^{n}))=O(2^{2n}n)\)的? 反正都能过, 不管了.

#include <cstdio>
#include <cstring>
#include <algorithm>
#define W 9
#define N 520
#define INF 0x3f3f3f3f
#define fo(i, a, b) for(int i = (a); i <= (b); ++i)
#define fd(i, a, b) for(int i = (a); i >= (b); --i)
using namespace std;
inline int read() // notice : 1. long long ? 2. negative ?
{
	int x = 0; char ch = getchar();
	while(ch < '0' || ch > '9')	ch = getchar();
	while(ch >= '0' && ch <= '9')	x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
	return x;
}
int n, p[N], w[N][N], f[N][N];
inline void pre(int l, int r)
{
	if(l == r)	return ;
	int mid = (l + r) >> 1;
	p[mid + 1] = mid - l + 1;
	pre(l, mid); pre(mid + 1, r);
}
int main()
{
//	freopen("mars.in", "r", stdin);
//	freopen(".out", "w", stdout);
	n = 1 << read();
	fo(i, 1, n)	fo(j, 1, n)	w[i][j] = read();
	pre(1, n);
	fo(i, 2, n)
		fo(j, 1, n)
		{
			f[i][j] = INF;
			int t = (j - 1) / p[i] + 1, l, r; t % 2 ? ++t : --t;
			r = t * p[i], l = r - p[i] + 1;
//			printf("%d %d %d %d\n", p[i], t, l, r);
			fo(k, l, r)	f[i][j] = min(f[i][j], f[i - 1][k] + w[k][j]);
		}
	int ans = INF;
	fo(i, 1, n)	ans = min(ans, f[n][i]);
	printf("%d", ans);
	return 0;
}

标签:ch,int,线段,mid,include,JZOJ3166,火星,define
来源: https://www.cnblogs.com/Martin-MHT/p/14731982.html