2019 Multi-University Training Contest 2 - 1011 - Keen On Everything But Triangle - 线段树

http://acm.hdu.edu.cn/showproblem.php?pid=6601

首先要贪心地想,题目要最长的边长,那么要怎么构造呢?在一段连续的区间里面,一定是拿出最长的三根出来比,这样一定是最大的(废话)。而且假如组成三角形失败的话最长的那根这次就没有用了。

考虑临界情况,也就是刚刚好不能组成三角形的时候,要在1e9内尽可能地安排多的棒子,那就不妨设为:1,1,2,3,5,8……也就是斐波那契数列。可以打出来发现在43项左右的时候已经接近1e9了。

也就是每个区间真正有用的只是最长的50根。

考虑对每个区间建立线段树,线段树里面就存当前区间最长的至多50根。

要动态管理这些区间vector实现的版本的确非常方便。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int MAXM = 100000;
int a[MAXM + 5];
vector<int> st[(MAXM << 2) + 5];

void build(int o, int l, int r) {
    if(l == r) {
        st[o].resize(r - l + 1);
        st[o][0] = a[l];
    } else {
        int m = (l + r) >> 1;
        build(o << 1, l, m);
        build(o << 1 | 1, m + 1, r);
        st[o].resize(min(100,r - l + 1));
        merge(st[o << 1].begin(), st[o << 1] .end(),
              st[o << 1 | 1].begin(), st[o << 1 | 1] .end(),
              st[o].begin(), greater<int>());
        st[o].resize(min(50, r - l + 1));
    }
}

int used[MAXM], usedtop, it[MAXM];

void query(int o, int l, int r, int a, int b) {
    if(a <= l && r <= b) {
        used[++usedtop] = o, it[usedtop] = 0;
    } else {
        int m = (l + r) >> 1;
        if(a <= m)
            query(o << 1, l, m, a, b);
        if(b >= m + 1)
            query(o << 1 | 1, m + 1, r, a, b);
    }
}

int val[MAXM], valtop;

ll answer() {
    valtop = 0;
    bool fail = 0;
    int t = 50;
    while(t-- && !fail) {
        fail = 1;
        int maxval = 0, maxid = 0;
        for(int i = 1; i <= usedtop; ++i) {
            if(it[i] < st[used[i]].size()) {
                if(st[used[i]][it[i]] > maxval) {
                    maxval = st[used[i]][it[i]];
                    maxid = i;
                }
                fail = 0;
            }
        }
        if(!fail) {
            val[++valtop] = maxval;
            ++it[maxid];
        }
    }
    for(int i = 1; i + 2 <= valtop; ++i)
        if(val[i + 1] + val[i + 2] > val[i])
            return (ll)val[i] + val[i + 1] + val[i + 2];
    return -1;
}

int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
#endif // Yinku
    int n, q;
    while(~scanf("%d%d", &n, &q)) {
        for(int i = 1; i <= n; ++i)
            scanf("%d", &a[i]);
        build(1, 1, n);
        for(int i = 1; i <= q; ++i) {
            int l, r;
            scanf("%d%d", &l, &r);
            usedtop = 0;
            query(1, 1, n, l, r);
            printf("%lld\n", answer());
        }
    }
    return 0;
}

不用vector的又慢又卡内存???或许跟手动的merge有关,把这个换成STL做对照可能会更快。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int MAXM = 100000;
int a[MAXM + 5], st[(MAXM << 2) + 5][50 + 5], sttop[(MAXM << 2) + 5];

int _merge(int a[], int as, int b[], int bs, int c[]) {
    int i = 1, j = 1, k = 1;
    while((k <= 50) && (i <= as || j <= bs)) {
        if(i > as) {
            c[k] = b[j];
            ++j;
            ++k;
        } else if(j > bs) {
            c[k] = a[i];
            ++i;
            ++k;
        } else if(a[i] >= b[j]) {
            c[k] = a[i];
            ++i;
            ++k;
        } else {
            c[k] = b[j];
            ++j;
            ++k;
        }
    }
    return k;
}

void build(int o, int l, int r) {
    if(l == r) {
        sttop[o] = 1;
        st[o][1] = a[l];
    } else {
        int m = (l + r) >> 1;
        build(o << 1, l, m);
        build(o << 1 | 1, m + 1, r);
        sttop[o] = _merge(st[o << 1], sttop[o << 1],
                          st[o << 1 | 1], sttop[o << 1 | 1],
                          st[o]);
//        printf("[%d,%d]\n", l, r);
//        for(int i = 1; i <= sttop[o]; ++i) {
//            printf(" %d", st[o][i]);
//        }
//        printf("\n\n");
    }
}

int used[MAXM], usedtop, it[MAXM];

void query(int o, int l, int r, int a, int b) {
    if(a <= l && r <= b) {
        used[++usedtop] = o, it[usedtop] = 1;
    } else {
        int m = (l + r) >> 1;
        if(a <= m)
            query(o << 1, l, m, a, b);
        if(b >= m + 1)
            query(o << 1 | 1, m + 1, r, a, b);
    }
}

int val[MAXM], valtop;

ll answer() {
    valtop = 0;
    bool fail = 0;
    int t = 50;
    while(t-- && !fail) {
        fail = 1;
        int maxval = 0, maxid = 0;
        for(int i = 1; i <= usedtop; ++i) {
            if(it[i] <= sttop[used[i]]) {
                if(st[used[i]][it[i]] > maxval) {
                    maxval = st[used[i]][it[i]];
                    maxid = i;
                }
                fail = 0;
            }
        }
        if(!fail) {
            val[++valtop] = maxval;
            ++it[maxid];
        }
    }
    for(int i = 1; i + 2 <= valtop; ++i)
        if(val[i + 1] + val[i + 2] > val[i])
            return (ll)val[i] + val[i + 1] + val[i + 2];
    return -1;
}

int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
#endif // Yinku
    int n, q;
    while(~scanf("%d%d", &n, &q)) {
        for(int i = 1; i <= n; ++i)
            scanf("%d", &a[i]);
        build(1, 1, n);
        for(int i = 1; i <= q; ++i) {
            int l, r;
            scanf("%d%d", &l, &r);
            usedtop = 0;
            query(1, 1, n, l, r);
            printf("%lld\n", answer());
        }
    }
    return 0;
}

草,更慢,更浪费内存。让我对vector刮目相看。看来预先resize的vector是速度足够惊人的。

posted @ 2019-07-24 19:56  韵意  阅读(223)  评论(0编辑  收藏  举报