题目链接:http://acdream.info/problem?pid=1076
这题DP的状态很好设计,dp[i][j]表示指令i的时候,全排列状态是j,全排列一共就120个,预处理出来就可以了
那么问题就在于对于一个指令怎么快速获得这个整个区间的置换乘积,这步其实利用一个线段树维护就可以了,但是要注意置换是不满足交换律的,所以正序逆序都要保存一遍
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n, m;
int to[55555], num[125][5];
int A[5];
void init() {
for (int i = 0; i < 5; i++) A[i] = i + 1;
int cnt = 0;
do {
int sum = 0;
for (int i = 0; i < 5; i++) {
sum = sum * 10 + A[i];
num[cnt][i] = A[i];
}
to[sum] = cnt++;
} while (next_permutation(A, A + 5));
}
const int N = 100005;
struct Node {
int l, r, s[2];
} node[N * 4];
#define lson(x) ((x<<1)+1)
#define rson(x) ((x<<1)+2)
int gao(int a, int b) {
int sum = 0;
for (int i = 0; i < 5; i++) {
A[i] = num[b][num[a][i] - 1];
sum = sum * 10 + A[i];
}
return to[sum];
}
void pushup(int x) {
node[x].s[0] = gao(node[lson(x)].s[0], node[rson(x)].s[0]);
node[x].s[1] = gao(node[rson(x)].s[1], node[lson(x)].s[1]);
}
void build(int l, int r, int x = 0) {
node[x].l = l; node[x].r = r;
if (l == r) {
int sum = 0, tmp;
for (int i = 0; i < 5; i++) {
scanf("%d", &tmp);
sum = sum * 10 + tmp;
}
node[x].s[0] = node[x].s[1] = to[sum];
return;
}
int mid = (l + r) / 2;
build(l, mid, lson(x));
build(mid + 1, r, rson(x));
pushup(x);
}
int get(int l, int r, int tp, int x = 0) {
if (node[x].l >= l && node[x].r <= r) return node[x].s[tp];
int mid = (node[x].l + node[x].r) / 2;
if (l <= mid && r > mid) {
if (tp == 0) return gao(get(l, r, tp, lson(x)), get(l, r, tp, rson(x)));
else return gao(get(l, r, tp, rson(x)), get(l, r, tp, lson(x)));
}
else if (l <= mid) return get(l, r, tp, lson(x));
else if (r > mid) return get(l, r, tp, rson(x));
}
const int INF = 0x3f3f3f3f;
int S[105], T[105], f[105];
int dp[105][125];
int main() {
init();
while (~scanf("%d%d", &n, &m)) {
build(1, n);
for (int i = 0; i <= m; i++)
for (int j = 0; j < 120; j++) dp[i][j] = INF;
for (int i = 1; i <= m; i++) {
scanf("%d%d", &S[i], &T[i]);
f[i] = 0;
if (S[i] > T[i]) {
swap(S[i], T[i]);
f[i] = 1;
}
}
int sum = 0, tmp;
for (int i = 0; i < 5; i++) {
scanf("%d", &tmp);
sum = sum * 10 + tmp;
}
dp[0][to[sum]] = 0;
for (int i = 1; i <= m; i++) {
int sb = get(S[i], T[i], f[i]);
for (int j = 0; j < 120; j++) {
if (dp[i - 1][j] == INF) continue;
int nxt = gao(j, sb);
dp[i][j] = min(dp[i][j], dp[i - 1][j]);
dp[i][nxt] = min(dp[i][nxt], dp[i - 1][j] + max(S[i] - T[i], T[i] - S[i]) + 1);
}
}
if (dp[m][to[12345]] == INF) printf("-1\n");
else printf("%d\n", dp[m][to[12345]]);
}
return 0;
}