【牛客挑战赛48 E】速度即转发

题目

题目链接:https://ac.nowcoder.com/acm/contest/11161/E
给定一个长度为 n n n 的数组 a a a。支持 m m m 次操作:

  • 给定 l , r , k l,r,k l,r,k,求 [ 0 , 1 0 5 ] [0,10^5] [0,105] 内满足 ∑ i = l r max ⁡ ( a i − x , 0 ) ≥ k \sum^{r}_{i=l}\max(a_i-x,0)\geq k i=lrmax(aix,0)k 的最大正整数 x x x
  • 给定 x , y x,y x,y,将 a x a_x ax 修改为 y y y

n , m , a i ≤ 1 0 5 , k ≤ 1 0 10 n,m,a_i\leq 10^5,k\leq 10^{10} n,m,ai105,k1010

思路

设值域为 A A A
一个直观的想法是直接分块,对于每一个块维护 s u m [ x ] , c n t [ x ] sum[x],cnt[x] sum[x],cnt[x] 表示不小于 x x x 的数字之和以及出现次数,但是这样每次修改是 O ( A ) O(A) O(A) 的,无法接受。
那么自然会想到再对值域分块,然后每次询问从大到小枚举。但是这样每次询问的复杂度又是 O ( n A ) O(\sqrt{nA}) O(nA ) 的,依然不可以接受。
所以考虑离线下来用带修莫队维护。设 s u m [ x ] , c n t [ x ] sum[x],cnt[x] sum[x],cnt[x] 表示值域在 [ x T , ( x + 1 ) T ) [xT,(x+1)T) [xT,(x+1)T) 区间的数字和以及出现次数,指针移动的复杂度是 O ( 1 ) O(1) O(1) 的,询问的时候易容从大到小枚举块,如果这个块符合要求,那么再枚举这个块内的所有数字即可。这样一次询问就是 O ( A ) O(\sqrt{A}) O(A ) 的。复杂度就得以保证了。
时间复杂度为 O ( n 5 3 + m A ) O(n^{\frac{5}{3}}+m\sqrt{A}) O(n35+mA )。我取序列块长为 2000 2000 2000,值域块长为 320 320 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值