HDU6601 Keen On Everything But Triangle 线段树或主席树
网址:https://vjudge.net/problem/HDU-6601
题意:
给出序列$a_1,a_2,a_3,......,a_n$代表棍子的长度,和$Q$次询问,对于第$i$次询问,在$l_i$和$r_i$的区间中选3根棍子构成三角形,输出三角形最大周长,如果组成不了或者根子不够,输出$-1$。
题解:
一、主席树做法:
直接对于每个区间查询第$1$,第$2$,$......$,一直到第$r-l+1$大,一旦发现成功的三角形则输出,如果都不行或者根子不够输出$-1$。
AC代码:
#include <bits/stdc++.h> using namespace std; const int MAXN=100005; struct chieftree { struct node { int l,r,sum; }; node tr[MAXN*20]; int rt[MAXN]; int cnt; void init() { cnt=0; } void build(int &rt,int l,int r) { rt=++cnt; tr[rt].sum=0; if(l==r) return; int m=(l+r)/2; build(tr[rt].l,l,m); build(tr[rt].r,m+1,r); } void update(int &rt,int l,int r,int val) { tr[++cnt]=tr[rt]; rt=cnt; ++tr[rt].sum; if(l==r) return; int m=(l+r)/2; if(val<=m) update(tr[rt].l,l,m,val); else update(tr[rt].r,m+1,r,val); } int query(int rl,int rr,int l,int r,int val) { int d=tr[tr[rr].r].sum-tr[tr[rl].r].sum; if(l==r) return l; int m=(l+r)/2; if(val<=d) return query(tr[rl].r,tr[rr].r,m+1,r,val); else return query(tr[rl].l,tr[rr].l,l,m,val-d); } }; chieftree tr; void print() { for(int i=1;i<=tr.cnt;++i) cout<<tr.tr[i].l<<" "<<tr.tr[i].r<<" "<<tr.tr[i].sum<<endl; } long long a[MAXN],b[MAXN]; long long ans(int l,int r,int n) { if(r-l+1<3) return -1; int pa=tr.query(tr.rt[l-1],tr.rt[r],1,n,1),pb=tr.query(tr.rt[l-1],tr.rt[r],1,n,2),pc,k=2; do{ pc=tr.query(tr.rt[l-1],tr.rt[r],1,n,++k); //cout<<b[pa]<<" "<<b[pb]<<" "<<b[pc]<<endl; if(b[pa]<b[pb]+b[pc]) return b[pa]+b[pb]+b[pc]; pa=pb; pb=pc; }while(k<r-l+1); return -1; } int main() { int n,m; while(~scanf("%d%d",&n,&m)) { for(int i=1;i<=n;++i) { scanf("%lld",&a[i]); b[i]=a[i]; } sort(b+1,b+1+n); int nnew=unique(b+1,b+1+n)-b-1; tr.init(); tr.build(tr.rt[0],1,nnew); for(int i=1;i<=n;++i) { tr.rt[i]=tr.rt[i-1]; int pos=lower_bound(b+1,b+1+nnew,a[i])-b; //cout<<pos<<endl; tr.update(tr.rt[i],1,nnew,pos); } //print(); int aa,bb; for(int i=0;i<m;++i) { scanf("%d%d",&aa,&bb); printf("%lld\n",ans(aa,bb,nnew)); } } return 0; }
二、线段树做法:
考虑到最坏情况,这个区间形成斐波那契数列,此时,到第$45$位时已经超出$1e9$,所以我们只要在线段树中保存前$50$大,然后查询时记录查询到的区间,再在这些区间中暴力找到前$44$大即可(vector一定要先确定容量再填充元素,不能push_back,否则会TLE)。
AC代码:
#include <bits/stdc++.h> //#pragma GCC optimize(2) using namespace std; #define MAXN 100005 int num[MAXN]; void print(vector<int> &vec) { for(auto &i:vec) cout<<" "<<i; cout<<endl; } int tagtop; struct SegTree { struct node { vector<int>val; int l,r; }; node tr[MAXN<<2]; void build(int l,int r,int k) { tr[k].l=l,tr[k].r=r; if(l==r) { tr[k].val.resize(1); tr[k].val[0]=num[l]; return; } int m=(l+r)/2; build(l,m,k<<1); build(m+1,r,(k<<1)+1); tr[k].val.resize(min(90,r-l+1)); merge(tr[k<<1].val.begin(),tr[k<<1].val.end(),tr[(k<<1)+1].val.begin(),tr[(k<<1)+1].val.end(), tr[k].val.begin(),greater<int>()); tr[k].val.resize(min(45,r-l+1)); } int tag[MAXN],tagpos[MAXN]; void query(int l,int r,int k) { if(l<=tr[k].l&&r>=tr[k].r) { tag[tagtop]=k,tagpos[tagtop++]=0;//tag标记区间,tagpos标记此时在该区间中查询到的值的位置 return; } int m=(tr[k].l+tr[k].r)/2;; if(l<=m) query(l,r,k<<1); if(r>m) query(l,r,(k<<1)+1); } int val[50],maxtop,maxval,maxpos; long long answer(int l,int r) { if(r-l+1<3) return -1; query(l,r,1); bool flag=0; int t=45; maxtop=0; while(t--&&!flag) { maxval=maxpos=0; flag=1; for(int i=0;i<tagtop;++i)//找最大值,对于每个已经记录的区间 { if(tagpos[i]<tr[tag[i]].val.size())//没有选完 { if(tr[tag[i]].val[tagpos[i]]>maxval)//记录最大值和所属区间 { maxval=tr[tag[i]].val[tagpos[i]]; maxpos=i; } flag=0; } } if(!flag) { ++tagpos[maxpos];//被选取了最大值的区间的选择标记后移一位 val[maxtop++]=maxval;//记录这个最大值 } } for(int i=2;i<maxtop;++i)//组合三角形 if(val[i-2]<val[i-1]+val[i]) return (long long)(val[i-2])+(long long)(val[i-1])+(long long)(val[i]); return -1; } }; SegTree tr; int main() { int n,m; while(scanf("%d%d",&n,&m)!=EOF) { for(int i=1;i<=n;++i) scanf("%d",&num[i]); tr.build(1,n,1); int l,r; for(int i=0;i<m;++i) { scanf("%d%d",&l,&r); tagtop=0; printf("%lld\n",tr.answer(l,r)); } } return 0; }