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