题目大意
你有一个数列 a1,a2,⋯ ,ana_1,a_2,\cdots,a_na1,a2,⋯,an。你可以进行这样的一次操作,每次选择数列中其中一个数然后将其除 222 下取整,也就是选择一个数 aia_iai,变成 ⌊ai2⌋\lfloor \frac{a_i}{2} \rfloor⌊2ai⌋。
一共有 qqq 个询问,每次你考虑数列中 [l,r][l,r][l,r] 这段数,即 al,al+1,al+2,⋯ ,ara_l,a_{l+1},a_{l+2},\cdots,a_ral,al+1,al+2,⋯,ar,对这些数字进行不超过 kkk 次操作,这些数字的总和最小值可能是多少。
1≤n≤105, 1≤q≤5∗1051 \leq n \leq 10^5,~1 \leq q \leq 5*10^51≤n≤105, 1≤q≤5∗105
1≤ai≤109, 0≤k≤1091 \leq a_i \leq 10^9,~0 \leq k \leq 10^91≤ai≤109, 0≤k≤109
5000 ms,256 MB
\\
\\
\\
题解
首先一个数字最多除 log\loglog 次,log\loglog 次以后就变成 000 了。
然后 aia_iai 每除一次 222,就相当于减去一个 ⌈ai2⌉\lceil \frac{a_i}{2} \rceil⌈2ai⌉。这样的话就可以把每个 aia_iai 拆成 log\loglog 个数字之和。
那么就相当于有 nnn 个物品,每个物品可以拆成 log\loglog 个小物品,每个小物品有一个收益。然后每次询问一个区间,取 kkk 个小物品,能获得的最大收益是多少。
没啥特殊情况的话那就是区间前 kkk 大和,主席树一下就行了。
然后发现空间只有 256 MB
各凭本事卡内存
——杜老师
题解的卡内存的姿势还是有丶巧妙的。
题解的姿势就是,把这个主席树拆开来,拆成若干个小一点的主席树。
怎么拆呢?就是把小物品按其价值的二进制位数分层,分成 log\loglog 层,每个大物品在每一层只会有一个小物品(每除一次有一个)。这样的话,每一层就只有 nnn 个物品了,那么每一层的主席树就只有 O(nloga)O(n \log a)O(nloga) 那么大了。
然后求区间前 kkk 大和,倒着枚举层(从高位到低位),每个询问只会在某一层处跑线段树二分,因此时间也还是 O(qloga)O(q \log a)O(qloga) 的。
代码
#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long LL;
const int maxn=1e5+5, maxq=5e5+5, MX=31;
int n,q,maxw,maxx,l[maxq],r[maxq],c[maxq];
LL s[maxn];
vector<pair<int,int>> v[MX+5];
int root[maxn],tot,son[maxn*40][2],num[maxn*40];
LL sum[maxn*40];
int New()
{
tot++;
num[tot]=sum[tot]=son[tot][0]=son[tot][1]=0;
return tot;
}
int tr_xg(int k,int last,int l,int r,int x)
{
while (l<r)
{
sum[k]=sum[last]+x, num[k]=num[last]+1;
int t1=(l+r)>>1;
if (x<=t1)
{
son[k][0]=New();
son[k][1]=son[last][1];
k=son[k][0], last=son[last][0], r=t1;
} else
{
son[k][0]=son[last][0];
son[k][1]=New();
k=son[k][1], last=son[last][1], l=t1+1;
}
}
sum[k]=sum[last]+x, num[k]=num[last]+1;
}
LL tr_cx(int k,int last,int l,int r,int x)
{
LL re=0;
while (l<r)
{
int t1=(l+r)>>1;
if (num[son[k][1]]-num[son[last][1]]>=x) k=son[k][1], last=son[last][1], l=t1+1;
else
{
re+=sum[son[k][1]]-sum[son[last][1]];
x-=num[son[k][1]]-num[son[last][1]];
k=son[k][0], last=son[last][0], r=t1;
}
}
return (x) ?re+(sum[k]-sum[last])/(num[k]-num[last])*x :re ;
}
LL ans[maxq];
int main()
{
scanf("%d %d",&n,&q);
fo(i,1,n)
{
scanf("%lld",&s[i]);
maxx=(s[i]>maxx) ?s[i] :maxx ;
int w=0;
for(int x=s[i]; x; x>>=1) w++;
maxw=max(maxw,w-1);
for(int cnt=w-1, x=s[i]; x; cnt--, x>>=1) v[cnt].push_back(make_pair(i,x-(x>>1)));
s[i]+=s[i-1];
}
fo(i,1,q)
{
scanf("%d %d %d",&l[i],&r[i],&c[i]);
ans[i]=s[r[i]]-s[l[i]-1];
}
fd(i,maxw,0)
{
tot=-1;
int last=New();
memset(root,0,sizeof(root));
for(auto p:v[i])
{
tr_xg(root[p.first]=New(),last,1,maxx,p.second);
last=root[p.first];
}
fo(i,1,n) if (!root[i]) root[i]=root[i-1];
fo(j,1,q) if (c[j])
{
int cnt=num[root[r[j]]]-num[root[l[j]-1]];
if (cnt>=c[j])
{
ans[j]-=tr_cx(root[r[j]],root[l[j]-1],1,maxx,c[j]);
c[j]=0;
} else
{
c[j]-=cnt;
ans[j]-=sum[root[r[j]]]-sum[root[l[j]-1]];
}
}
}
fo(i,1,q) printf("%lld\n",ans[i]);
}