概述
题目大意
现有长度为N的两个数组a和s
有N个询问,第k个询问你需要找出一个i使得s[i]*2+a[i]+1..i-1中a数组前k-1大的值之和最大。
整体二分
注意到每个询问找出的那个最优的i是满足单调性的
因此可以对所有询问整体二分
用solve(l1,r1,l2,r2)表示当前处理第l1~r1个询问,他们最优的i范围在l2~r2。
令mid1=(l1+r1)/2,我们在l2~r2则找,看看那个是对于第mid1的最优i。找出最优的i设为mid2,那么分为solve(l1,mid1-1,l2,mid2),solve(mid1+1,r1,mid2,r2)。
我们易得知每一层的循环复杂度总和都是n,一共log n层,则复杂度为n log n。
主席树
如何得知第X个询问选择的i为y时的答案?
前y-1中最大的x-1个可以用主席树得到。
参考程序
#include<cstdio>
#include<cstring>
#include<cstdlib>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const ll maxn=100000+10;
ll i,j,k,l,t,n,m,tot;
ll sum[maxn*25],num[maxn*25],left[maxn*25],right[maxn*25],root[maxn],ans[maxn],s[maxn],a[maxn];
void insert(ll l,ll r,ll y,ll p,ll &x){
x=++tot;
sum[x]=sum[y]+p;
num[x]=num[y]+1;
left[x]=left[y];right[x]=right[y];
if (l==r) return;
ll mid=(l+r)/2;
if (p<=mid) insert(l,mid,left[y],p,left[x]);
else insert(mid+1,r,right[y],p,right[x]);
}
ll get(ll l,ll r,ll x,ll y){
if (y>num[x]) return -10000;
if (!y) return 0;
if (l==r) return sum[x]/num[x]*y;
ll mid=(l+r)/2;
if (num[right[x]]>=y) return get(mid+1,r,right[x],y);
else return sum[right[x]]+get(l,mid,left[x],y-num[right[x]]);
}
void solve(ll l1,ll r1,ll l2,ll r2){
if (l1>r1) return;
ll mid1=(l1+r1)/2,mid2,t=0,k;
ll i;
fo(i,l2,r2){
k=s[i]*2+a[i]+get(1,999,root[i-1],mid1-1);
if (k>t){
t=k;
mid2=i;
}
}
ans[mid1]=t;
solve(l1,mid1-1,l2,mid2);solve(mid1+1,r1,mid2,r2);
}
int main(){
freopen("salesman.in","r",stdin);freopen("salesman.out","w",stdout);
scanf("%lld",&n);
fo(i,1,n) scanf("%lld",&s[i]);
fo(i,1,n){
scanf("%lld",&a[i]);
insert(1,999,root[i-1],a[i],root[i]);
}
solve(1,n,1,n);
fo(i,1,n) printf("%lldn",ans[i]);
fclose(stdin);fclose(stdout);
return 0;
}
最后
以上就是腼腆火车为你收集整理的[NOIP2015]推销员题目大意整体二分主席树参考程序的全部内容,希望文章能够帮你解决[NOIP2015]推销员题目大意整体二分主席树参考程序所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复