现有长度为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("%lld\n",ans[i]);
fclose(stdin);fclose(stdout);
return 0;
}