这是今年的福建省选原题……按说不是很难但我却卡了很长时间……
我的写法十分诡异难以调试……以后得换一种写法了
题解直接抄一下懒得写了
------------------------------
这个模型还是比较常见的。对于给定序列,我们要求的是最小的x,使所有<=x的数的和比x小。
设不同权值个数为t,我们用可持久化建出t棵线段树,每棵线段树均以下标为关键字,以支持查询小于等于某一权值的数在给定区间的和为多少。
那么在区间[L, R]中,假设答案为x0。我们可在线段树中查询[L, R]区间内所有不超过x0的数的和S。假如S小于x0,x0就是答案;否则答案一定至少为S + 1。迭代即可。
复杂度O(nlogn + kmlogn),k为迭代次数,不超过45(最坏情况由斐波那契数列卡到)
------------------------------
我真的是严格按照这个题解写的!但是其他人的写法都和题解不一样……所以我很诧异……UPD:其实是一样的!只是细节有点区别!我真是太弱了!
#include<cstdio> #include<algorithm> #include<cstring> #include<vector> using namespace std; int n,siz,a[100005],b[100005],root[100005],bn,m; vector<int> V[100005]; struct SegTree{ int nl,nr,l,r,sum,cre; }tree[4000005]; void Add(int &rt,int lastrt,int l,int r,int pos,int val,int Rev){ if(Rev!=tree[rt].cre){rt=++siz;tree[rt].nl=tree[lastrt].nl;tree[rt].nr=tree[lastrt].nr;tree[rt].l=l;tree[rt].r=r;tree[rt].cre=Rev;} if(l==r){tree[rt].sum+=val;return;} int mid=l+r>>1; if(pos<=mid)Add(tree[rt].nl,tree[lastrt].nl,l,mid,pos,val,Rev); if(pos>mid)Add(tree[rt].nr,tree[lastrt].nr,mid+1,r,pos,val,Rev); tree[rt].sum=tree[tree[rt].nl].sum+tree[tree[rt].nr].sum; } int Sum(int rt,int l,int r){ if(rt==0)return 0; if(l<=tree[rt].l && tree[rt].r<=r){ return tree[rt].sum; } int mid=tree[rt].l+tree[rt].r>>1,ans=0; if(l<=mid)ans+=Sum(tree[rt].nl,l,r); if(r>mid)ans+=Sum(tree[rt].nr,l,r); return ans; } int main(){ freopen("4408.in","r",stdin); freopen("4408.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;i++){scanf("%d",&a[i]);b[i]=a[i];} sort(b+1,b+n+1); bn=unique(b+1,b+n+1)-b-1; for(int i=1;i<=n;i++){a[i]=lower_bound(b+1,b+bn+1,a[i])-b;V[a[i]].push_back(i);} for(int i=1;i<=bn;i++){for(int j=0;j<V[i].size();j++)Add(root[i],root[i-1],1,n,V[i][j],b[a[V[i][j]]],i);} scanf("%d",&m); for(int i=1;i<=m;i++){ int l,r; scanf("%d %d",&l,&r); int ans=0,tmp=-1; while(tmp<=ans){ tmp=ans+1; ans=Sum(root[upper_bound(b+1,b+bn+1,tmp)-b-1],l,r); } printf("%d\n",ans+1); } return 0; }