【牛客挑战赛48 E】速度即转发
题目
题目链接:https://ac.nowcoder.com/acm/contest/11161/E
给定一个长度为 \(n\) 的数组 \(a\)。支持 \(m\) 次操作:
- 给定 \(l,r,k\),求 \([0,10^5]\) 内满足 \(\sum^{r}_{i=l}\max(a_i-x,0)\geq k\) 的最大正整数 \(x\)。
- 给定 \(x,y\),将 \(a_x\) 修改为 \(y\)。
\(n,m,a_i\leq 10^5,k\leq 10^{10}\)。
思路
设值域为 \(A\)。
一个直观的想法是直接分块,对于每一个块维护 \(sum[x],cnt[x]\) 表示不小于 \(x\) 的数字之和以及出现次数,但是这样每次修改是 \(O(A)\) 的,无法接受。
那么自然会想到再对值域分块,然后每次询问从大到小枚举。但是这样每次询问的复杂度又是 \(O(\sqrt{nA})\) 的,依然不可以接受。
所以考虑离线下来用带修莫队维护。设 \(sum[x],cnt[x]\) 表示值域在 \([xT,(x+1)T)\) 区间的数字和以及出现次数,指针移动的复杂度是 \(O(1)\) 的,询问的时候易容从大到小枚举块,如果这个块符合要求,那么再枚举这个块内的所有数字即可。这样一次询问就是 \(O(\sqrt{A})\) 的。复杂度就得以保证了。
时间复杂度为 \(O(n^{\frac{5}{3}}+m\sqrt{A})\)。我取序列块长为 \(2000\),值域块长为 \(320\)。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef long long ll;
const int N=100010,M1=2000,M2=320;
int n,m,sb,a[N],bel[N],X[N],Y[N],cnt[M2+1],res[N];
ll sum[M2+1],ans[N];
struct Query
{
int l,r,k,t,id;
friend bool operator <(Query x,Query y)
{
if (bel[x.l]!=bel[y.l]) return bel[x.l]<bel[y.l];
if (bel[x.r]!=bel[y.r]) return bel[x.r]<bel[y.r];
return x.t<y.t;
}
}ask[N];
int read()
{
int d=0; char ch=getchar();
while (!isdigit(ch)) ch=getchar();
while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
return d;
}
void write(ll x)
{
if (x>9) write(x/10);
putchar(x%10+48);
}
void add(int x)
{
sum[bel[a[x]]]+=a[x];
cnt[bel[a[x]]]++;
res[a[x]]++;
}
void del(int x)
{
sum[bel[a[x]]]-=a[x];
cnt[bel[a[x]]]--;
res[a[x]]--;
}
signed main()
{
n=read(); m=read();
for (int i=1;i<=n;i++)
{
a[i]=read();
bel[i]=(i-1)/M1+1;
}
for (int i=1,j=0,opt;i<=m;i++)
{
opt=read();
if (opt==0) sb++,ask[sb]=(Query){read(),read(),read(),j,sb};
if (opt==1) j++,X[j]=read(),Y[j]=read();
}
memset(ans,-1,sizeof(ans));
sort(ask+1,ask+1+sb);
for (int i=0;i<=100000;i++) bel[i]=i/M2;
for (int o=1,l=1,r=0,j=0;o<=sb;o++)
{
int ql=ask[o].l,qr=ask[o].r,k=ask[o].k,t=ask[o].t;
for (;l>ql;l--) add(l-1);
for (;r<qr;r++) add(r+1);
for (;l<ql;l++) del(l);
for (;r>qr;r--) del(r);
for (;j<t;j++)
{
if (l<=X[j+1] && r>=X[j+1]) del(X[j+1]);
swap(a[X[j+1]],Y[j+1]);
if (l<=X[j+1] && r>=X[j+1]) add(X[j+1]);
}
for (;j>t;j--)
{
if (l<=X[j] && r>=X[j]) del(X[j]);
swap(a[X[j]],Y[j]);
if (l<=X[j] && r>=X[j]) add(X[j]);
}
ll s=0,c=0;
for (int i=M2;i>=0;i--)
{
if (i*M2>=100000) continue;
s+=sum[i]; c+=cnt[i];
if (s-c*i*M2>=k)
{
for (int j=i*M2;j<min(100001LL,(i+1)*M2);j++)
{
if (s-j*c<k) { ans[ask[o].id]=j-1; break; }
s-=res[j]*j; c-=res[j];
}
if (ans[ask[o].id]==-1) ans[ask[o].id]=min(100001LL,(i+1)*M2)-1;
break;
}
}
}
for (int i=1;i<=sb;i++)
if (ans[i]!=-1) write(ans[i]),putchar(10);
else putchar('-'),putchar(49),putchar(10);
return 0;
}