ACdream 简单数据结构 专题

A - 风之国

题意: 给出X轴上N个点的坐标   X轴上的点按大小两两互相连通 现在要必须干掉K条路 求最小花费

分析: dp+线段树优化 我表示不会 copy一遍题解

         首先题目给出的城市坐标不是按X轴升序的,  那么我们就将每个城市根据坐标从左到右映射到X轴上的1~n

         设dp[i]:= 到第 i 个点(包括第i个点)时,  处理掉前面所有必须不连通的道路的最小花费。

         由于道路可能存在包含关系,  此时必定是选取最右边的左端点L作为区间左端点(假设每条道路从左端点开始, 右端点结束)

         否则选取其他的点只会徒增花费

         那么 dp[i] = min{dp[j] + len( j , j+1) }, L <= j < i, 其中len(j , j+1) 表示映射到坐标轴上的城市的距离

         我们用线段树来维护dp[j] + len( j , j+1) 的最小值    每次处理完后将dp[i] + len (i, i+1) 插入到i

         然后就可以ac了

代码:

//
//  Created by TaoSama on 2015-09-21
//  Copyright (c) 2015 TaoSama. All rights reserved.
//
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
#define pr(x) cout << #x << " = " << x << "  "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

int minv[N << 2];

#define root 1, n, 1
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1

void push_up(int rt) {
    minv[rt] = min(minv[rt << 1], minv[rt << 1 | 1]);
}

void update(int o, int v, int l, int r, int rt) {
    if(l == r) {
        minv[rt] = v;
        return;
    }
    int m = l + r >> 1;
    if(o <= m) update(o, v, lson);
    else update(o, v, rson);
    push_up(rt);
}

int query(int L, int R, int l, int r, int rt) {
    if(L <= l && r <= R) return minv[rt];
    int m = l + r >> 1, ret = INF;
    if(L <= m) ret = min(ret, query(L, R, lson));
    if(R > m) ret = min(ret, query(L, R, rson));
    return ret;
}

int n, k, a[N], all[N], pre[N];

int main() {
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);

    while(scanf("%d%d", &n, &k) == 2) {
        memset(minv, 0x3f, sizeof minv);
        memset(pre, 0, sizeof pre);

        for(int i = 1; i <= n; ++i) {
            scanf("%d", a + i);
            all[i] = a[i];
        }
        sort(all + 1, all + 1 + n);
        for(int i = 1; i <= k; ++i) {
            int l, r; scanf("%d%d", &l, &r);
            l = lower_bound(all + 1, all + 1 + n, a[l]) - all;
            r = lower_bound(all + 1, all + 1 + n, a[r]) - all;
            if(l > r) swap(l, r);
            pre[r] = max(pre[r], l);
        }

        int pos = 0, ans = 0;
        for(int i = 1; i <= n; ++i) {
            pos = max(pos, pre[i]);
            if(pos && i > 1) ans = query(pos, i - 1, root);
            if(i < n) update(i, ans + all[i + 1] - all[i], root);
        }
        printf("%d\n", ans);
    }
    return 0;
}

B - 真正的AC自动机

题意: 每个ACMer有能力, 价格,  每个题目有难度 现在小Z有L钱 问怎么请ACMer才能最少的天数完成

分析: 贪心啦 红果果的set贪心 将ACMer和题目按照 能力、难度大到小排序 先搞大的 大的都不能搞还搞毛

         把可以解决当前题目的ACMer都丢进set里 然后找一个最便宜的 如此贪心

         天数二分就可以了 能干掉这个题 继续干掉比它小的天数-1个题

代码: 据说PQ会快点 set习惯了

//
//  Created by TaoSama on 2015-09-21
//  Copyright (c) 2015 TaoSama. All rights reserved.
//
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
#define pr(x) cout << #x << " = " << x << "  "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

int n, m, l, ans[N];
struct ACM {
    int a, c, id;
    bool operator< (const ACM& rhs) const {
        return a > rhs.a;
    }
} A[N], B[N];

typedef pair<int, int> P;

bool check(int x) {
    set<P> s;
    int cost = 0;
    for(int i = 1, j = 1; i <= m;) {
        while(j <= n && A[j].a >= B[i].a) s.insert(P(A[j].c, A[j].id)), j++;
        if(!s.size()) return false;
        cost += s.begin()->first;
        if(cost > l) return false;
        int t = x, id = s.begin()->second;
        s.erase(s.begin());
        while(t-- && i <= m) ans[B[i++].id] = id;
    }
    return true;
}

int main() {
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);

    while(scanf("%d%d%d", &n, &m, &l) == 3) {
        for(int i = 1; i <= n; ++i) scanf("%d", &A[i].a);
        for(int i = 1; i <= n; ++i) scanf("%d", &A[i].c), A[i].id = i;
        for(int i = 1; i <= m; ++i) scanf("%d", &B[i].a), B[i].id = i;
        sort(A + 1, A + 1 + n);
        sort(B + 1, B + 1 + m);

        int l = 0, r = 1e5;
        while(l <= r) {
            int m = l + r >> 1;
            if(check(m)) r = m - 1;
            else l = m + 1;
        }
        if(check(l)) {
            puts("Good Luck");
            for(int i = 1; i <= m; ++i) printf("%d%c", ans[i], " \n"[i == m]);
        } else puts("Do it yourself");
    }
    return 0;
}

C - 最大的矩形

题意: 求连续的最大矩形

分析: 单调栈或者dp维护从i这个位置能向左右延伸的最大值  不同的是单调栈是() dp是[]

代码:

//
//  Created by TaoSama on 2015-09-21
//  Copyright (c) 2015 TaoSama. All rights reserved.
//
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <stack>
#include <string>
#include <set>
#include <vector>

using namespace std;
#define pr(x) cout << #x << " = " << x << "  "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

int n, a[N];
int l[N], r[N];

int main() {
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);

    while(scanf("%d", &n) == 1) {
        for(int i = 1; i <= n; ++i) scanf("%d", a + i);

        stack<int> s;
        for(int i = 1; i <= n; ++i) {
            while(s.size() && a[s.top()] >= a[i]) s.pop();
            l[i] = s.size() ? s.top() : 0;
            s.push(i);
        }
        while(s.size()) s.pop();
        for(int i = n; i >= 1; --i) {
            while(s.size() && a[s.top()] >= a[i]) s.pop();
            r[i] = s.size() ? s.top() : n + 1;
            s.push(i);
        }

        long long ans = 0;
        for(int i = 1; i <= n; ++i)
            ans = max(ans, 1LL * a[i] * (r[i] - l[i] - 1));
        printf("%lld\n", ans);
    }
    return 0;
}

D - 爱探险的小Z

题意: 长度为N的环 求长度不超过M的最大连续和以及位置  多解输出最短的优先 然后字典序小的优先

分析: 线段树区间合并 直接维护答案直接搞 倍增一下查询N次M大小的区间 更新答案就好

代码: 

//
//  Created by TaoSama on 2015-09-21
//  Copyright (c) 2015 TaoSama. All rights reserved.
//
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
#define pr(x) cout << #x << " = " << x << "  "
#define prln(x) cout << #x << " = " << x << endl
const int N = 2e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

int n, k, a[N];

#define root 1, n, 1
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1

typedef pair<int, int> Interval;

struct Node {
    int l, r;
    int preR, sufL;
    Interval sub;
} dat[N << 2];

int sum(const Interval& x) {
    return a[x.second] - a[x.first - 1];
}

Interval getMax(const Interval& x, const Interval& y) {
    int sx = sum(x), sy = sum(y);
    if(sx != sy) return sx > sy ? x : y;
    int dx = x.second - x.first, dy = y.second - y.first;
    if(dx != dy) return dx < dy ? x : y;
    return x < y ? x : y;
}

Node push_up(const Node& x, const Node& y) {
    Node ret;
    ret.l = x.l; ret.r = y.r;
    ret.preR = getMax(Interval(x.l, x.preR), Interval(x.l, y.preR)).second;
    ret.sufL = getMax(Interval(y.sufL, y.r), Interval(x.sufL, y.r)).first;
    ret.sub = getMax(Interval(x.sufL, y.preR), getMax(x.sub, y.sub));
    return ret;
}

void build(int l, int r, int rt) {
    if(l == r) {
        dat[rt].l = dat[rt].r = l;
        dat[rt].preR = dat[rt].sufL = l;
        dat[rt].sub = Interval(l, l);
        return;
    }
    int m = l + r >> 1;
    build(lson);
    build(rson);
    dat[rt] = push_up(dat[rt << 1], dat[rt << 1 | 1]);
}

Node query(int L, int R, int rt) {
    if(L <= dat[rt].l && dat[rt].r <= R) return dat[rt];
    int m = dat[rt].l + dat[rt].r >> 1;
    Node ret;
    if(L <= m && R > m)
        return push_up(query(L, R, rt << 1), query(L, R, rt << 1 | 1));
    else if(L <= m) ret = query(L, R, rt << 1);
    else if(R > m) ret = query(L, R, rt << 1 | 1);
    return ret;
}

int main() {
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);

    while(scanf("%d%d", &n, &k) == 2) {
        for(int i = 1; i <= n; ++i) {
            scanf("%d", a + i);
            a[i + n] = a[i];
        }
        n <<= 1;
        for(int i = 1; i <= n; ++i) a[i] += a[i - 1];
        build(root);

        Interval ans(1, 1);
        n >>= 1;
        for(int i = 1; i <= n; ++i)
            ans = getMax(ans, query(i, i + k - 1, 1).sub);
        printf("%d %d %d\n", sum(ans), (ans.first - 1) % n + 1,
               (ans.second - 1) % n + 1);
    }
    return 0;
}
分析: 考虑最大连续和=整个序列的前缀和-当前M大小区间的最小前缀和

          我们维护一个M+1大小的单调队列 - - 多一个好写代码 减的时候把这个多的减掉就好了

          注意特判全为负数的情况 样例很强就不多说了

代码:

//
//  Created by TaoSama on 2015-09-22
//  Copyright (c) 2015 TaoSama. All rights reserved.
//
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
#define pr(x) cout << #x << " = " << x << "  "
#define prln(x) cout << #x << " = " << x << endl
const int N = 2e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

int n, k;
int a[N];
int deq[N], deqv[N];

int main() {
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);

    while(scanf("%d%d", &n, &k) == 2) {
        for(int i = 1; i <= n; ++i) {
            scanf("%d", a + i);
            a[i + n] = a[i];
        }

        int l = 0, r = 0;
        int L = 1, R = 1, ans = a[1], sum = 0;
        for(int i = 2; i <= n; ++i)
            if(a[i] > ans) ans = a[i], L = R = i;
        for(int i = 1; i <= n << 1; ++i) {
            sum += a[i];
            while(l < r && deqv[r - 1] >= sum) --r;
            deq[r] = i; deqv[r] = sum; ++r;
            if(i > k) {
                if(i != deq[l]) {
                    if(sum - deqv[l] > ans || sum - deqv[l] == ans && i - deq[l] < R - L + 1) {
                        ans = sum - deqv[l];
                        L = deq[l] + 1;
                        R = i;
                    }
                }
                if(deq[l] == i - k) ++l;
            }
        }
        printf("%d %d %d\n", ans, (L - 1) % n + 1, (R - 1) % n + 1);
    }
    return 0;
}

E - 爱爬山的小Z

题意: 单点更新  环上区间RMQ

分析: 线段树模版题  - - 题目没说人走的方向啊 所以l>r是可以的

代码:

//
//  Created by TaoSama on 2015-09-21
//  Copyright (c) 2015 TaoSama. All rights reserved.
//
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
#define pr(x) cout << #x << " = " << x << "  "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

int n, q;
int maxv[N << 2];

#define root 1, n, 1
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1

void push_up(int rt) {
    maxv[rt] = max(maxv[rt << 1], maxv[rt << 1 | 1]);
}

void build(int l, int r, int rt) {
    if(l == r) {
        scanf("%d", &maxv[rt]);
        return;
    }
    int m = l + r >> 1;
    build(lson);
    build(rson);
    push_up(rt);
}

void update(int o, int v, int l, int r, int rt) {
    if(l == r) {
        maxv[rt] = v;
        return;
    }
    int m = l + r >> 1;
    if(o <= m) update(o, v, lson);
    else update(o, v, rson);
    push_up(rt);
}

int query(int L, int R, int l, int r, int rt) {
    if(L <= l && r <= R) {
        return maxv[rt];
    }
    int m = l + r >> 1, ret = 0;
    if(L <= m) ret = max(ret, query(L, R, lson));
    if(R > m) ret = max(ret, query(L, R, rson));
    return ret;
}

int main() {
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);

    while(scanf("%d", &n) == 1) {
        build(root);
        scanf("%d", &q);
        while(q--) {
            int op, x, y; scanf("%d%d%d", &op, &x, &y);
            if(op) {
                int ans = INF;
                if(x > y) swap(x, y);
                ans = min(ans, query(x, y, root));
                ans = min(ans, max(query(1, x, root), query(y, n, root)));
                printf("%d\n", ans);
            } else update(x, y, root);
        }
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值