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); } } }