hdu多校第二场1011 (hdu6601) Keen On Everything But Triangle 主席树

题意:

给定一个数列,每次询问一个区间,问这个区间中的值可组成的周长最大的三角形的周长。

题解:

定理1:给定一些值,这些值中组成边长最大的三角形的三条边的大小排名一定是连续的。

证明:假如第k大,第k+1大,第k+2+b(b>0)大的三条边组成了一个边长最大的三角形,那么较小的两条边加起来长度大于第三边,又因为第k+2大的边比第k+2+b大的边长,因此把第k+2+b大的边换成第k+2大的边组成的三角形边长一定比原来大,矛盾。

定理2:如果三角形边长被限制为1e9以内的正整数,那么如果某组值存在周长最大的三角形,一定由前45大的边组成

证明:假如三角形由第44,45,46大的边组成,那么由定理1,前45大的边都组不成三角形,不妨令这个已组成的三角形的周长最小,三边分别为1,1,1,那么,如果要让第k,k+1,k+2大的三条边组不成三角形,必须令第k大的边大于等于后两条边之和。不难推出,第k大的边大于等于Febnacci[46-k],最大的边至少为Feb[45],已经超出1e9的范围,矛盾

有了这两个命题,此题的解法就一目了然,对于每个询问,找出该区间内前45大的值,依次询问排名连续的三个是否能构成三角形。

用主席树维护区间第k大,复杂度O(qlogn*c)    (c=45)

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int MAXN = 1e5 + 10;
struct node {
    int ls, rs, sum;
    //左子树,右子树,该节点值 
} ns[MAXN * 20];
 
int ct;
//时间节点 
int rt[MAXN * 20];
//rt[i]代表a[i]在树上是第几个添加的 

void cpy(int& now, int old) {
    now = ++ct;
    ns[now] = ns[old];
}

void pushUp(int& now) {
    ns[now].sum = ns[ns[now].ls].sum + ns[ns[now].rs].sum;
}

void build(int& now, int l, int r) {
    now = ++ct;
    ns[now].sum = 0;
    if (l == r) return;
    int m = (l + r) >> 1;
    build(ns[now].ls, l, m);
    build(ns[now].rs, m + 1, r);
}

void update(int& now, int old, int l, int r, int x) { 
    cpy(now, old);
    //在旧树上添加新树 
    if (l == r) {
        ns[now].sum++;
        return;
    }
    int m = (l + r) >> 1;
    if (x <= m) update(ns[now].ls, ns[old].ls, l, m, x);
    else update(ns[now].rs, ns[old].rs, m + 1, r, x);
    pushUp(now);
    //向上更新节点权值 
}

int query(int s, int t, int l, int r, int k) {
    if (l == r) return l;
    int m = (l + r) >> 1;
    int cnt = ns[ns[t].ls].sum - ns[ns[s].ls].sum;
    //cout << s << " " << t << " " << cnt << endl;
    if (k <= cnt) return query(ns[s].ls, ns[t].ls, l, m, k);
    return query(ns[s].rs, ns[t].rs, m + 1, r, k - cnt);
    // 
}

void init(int n) {
    ct = 0;
    build(rt[0], 1, n);
    //从头开始建立主席树 
}

int a[MAXN], b[MAXN];
//b数组是a数组去重后的结果
 
int solve(int n,int m) {

//        int n, m;
//        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            b[i] = a[i];
        }
        sort(b + 1, b + n + 1);
        int sz = unique(b + 1, b + 1 + n) - b - 1;
        init(sz);
        //初始化树 
        for (int i = 1; i <= n; i++) {
            a[i] = lower_bound(b + 1, b + 1 + sz, a[i]) - b;
            //找出当前要添加的节点是b的第几个 
            update(rt[i], rt[i - 1], 1, sz, a[i]);
            //更新这个点 
        }
        /*for (int i = 0; i <= 5 * n; i++) {
            printf("%d, rt = %d, ls = %d, rs = %d, sum = %d\n", i, rt[i], ns[rt[i]].ls, ns[rt[i]].rs, ns[rt[i]].sum);
        }*/
        while (m--) {
            int ll,rr;
            scanf("%d %d",&ll,&rr);
            vector<int> vec;
            vec.clear();
            for(int i=1;i<=min(45,rr-ll+1);i++){
                vec.push_back(b[query(rt[ll-1],rt[rr],1,sz,rr-ll+2-i)]);
//                printf("push%d\n",vec[vec.size()-1]);
            }
            LL ans=-1;
            for(int i=2;i<vec.size();i++){
                if(vec[i-2]<vec[i-1]+vec[i]){
                    ans=1LL*vec[i-2]+vec[i]+vec[i-1];
                    break; 
                }
            }
            printf("%lld\n",ans);
//            printf("%d\n", b[query(rt[s - 1], rt[t], 1, sz, k)]);
        }
    return 0;
}
int main(){
    int n,m;
    while(1){
        int rep=scanf("%d %d",&n,&m);
        if(rep==-1)break;
        else {
//            memset()
            solve(n,m);
        }
    }
}

 

posted @ 2019-07-26 10:06  Isakovsky  阅读(190)  评论(0编辑  收藏  举报