Acdream 1076 XXX的机器人(dp + 线段树)

题目链接: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;
}
时间: 2024-10-11 21:50:28

Acdream 1076 XXX的机器人(dp + 线段树)的相关文章

题解 HDU 3698 Let the light guide us Dp + 线段树优化

http://acm.hdu.edu.cn/showproblem.php?pid=3698 Let the light guide us Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 62768/32768 K (Java/Others) Total Submission(s): 759    Accepted Submission(s): 253 Problem Description Plain of despair was

[后缀数组+dp/AC自动机+dp+线段树] hdu 4117 GRE Words

题意: 给你N个字符串, N(1 <= N <= 2w), 所有串的长度加一起不超过30w.每个串有个值.这个值[-1000, 1000]. 问不打乱字符串顺序,从中取若干个字符串,使得前一个串是后一个串的子串,求满足前面调条件的字符串值得和最大,求这个值. 思路: 其实就是一个很明显的dp. dp[i]代表以第i个字符串结尾的最大权值. 但是就是子串这个问题怎么处理. 由于这题数据比较水可以用后缀数组处理这个问题. 将所有字符串拼接,做sa. 每次在height数组里往上和往下寻找公共前缀等

HDU4719-Oh My Holy FFF(DP线段树优化)

Oh My Holy FFF Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others) Total Submission(s): 606    Accepted Submission(s): 141 Problem Description N soldiers from the famous "*FFF* army" is standing in a line, from le

hdu3698 Let the light guide us dp+线段树优化

http://acm.hdu.edu.cn/showproblem.php?pid=3698 Let the light guide us Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 62768/32768 K (Java/Others) Total Submission(s): 821    Accepted Submission(s): 285 Problem Description Plain of despair was

hdu 4719 Oh My Holy FFF(dp线段树优化)

Oh My Holy FFF Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others) Total Submission(s): 848    Accepted Submission(s): 219 Problem Description N soldiers from the famous "*FFF* army" is standing in a line, from le

hdu5293 Tree chain problem 树形dp+线段树

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5293 在一棵树中,给出若干条链和链的权值.求选取不相交的链使得权值和最大. 比赛的时候以为是树链剖分就果断没去想,事实上是没思路. 看了题解,原来是树形dp.话说多校第一场树形dp还真多. . .. 维护d[i],表示以i为根节点的子树的最优答案. sum[i]表示i的儿子节点(仅仅能是儿子节点)的d值和. 那么答案就是d[root]. 怎样更新d值 d[i] = max(sum[i] , w[p]+s

51Nod 欢乐手速场1 A Pinball[DP 线段树]

Pinball xfause (命题人) 基准时间限制:1 秒 空间限制:262144 KB 分值: 20 Pinball的游戏界面由m+2行.n列组成.第一行在顶端.一个球会从第一行的某一列出发,开始垂直下落,界面上有一些漏斗,一共有m个漏斗分别放在第2~m+1行,第i个漏斗的作用是把经过第i+1行且列数在Ai~Bi之间的球,将其移到下一行的第Ci列. 使用第i个漏斗需要支付Di的价钱,你需要保留一些漏斗使得球无论从第一行的哪一列开始放,都只可能到达第m+2行的唯一 一列,求花费的最少代价.

【uva1502/hdu4117-GRE Words】DP+线段树优化+AC自动机

这题我的代码在hdu上AC,在uva上WA. 题意:按顺序输入n个串以及它的权值di,要求在其中选取一些串,前一个必须是后一个的子串.问d值的和最大是多少. (1≤n≤2×10^4 ,串的总长度<=3*10^5) 题解: 这题一开始我的方向就错了,想了很久d[x][y]表示在AC自动机上的节点x.下一个串要大于y的dp.然而这样做数组要10^4*10^5=10^9级别,开都开不了,妥妥超时. 后来看了一眼题解...觉得自己智商真是感人... 用f[i]表示以第i个串为结尾的时候最大的d值,这样做

bzoj 1835 [ZJOI2010]base 基站选址(DP+线段树)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1835 [题意] 有n个村庄,每个村庄位于d[i],要求建立不多于k个基站,在第i个村庄建基站的费用为c[i],如果在距离村i不超过s[i]内有基站则该村被覆盖,村i不被覆盖的补偿费为w[i],求最少花费. [思路] 设f[i][j]表示第i个村建第j个基站的最小花费,则有转移式: f[i][j]=min{ f[k][j-1]+cost(k,i) } + c[i] ,j-1<=k<=