P2048 [NOI2010] 超级钢琴

Link\text{Link}

题意

给出一个长度为 nn 的数组 A1nA_{1\sim n},求区间和前 kk 大的长度在 [L,R][L,R] 之间的区间的区间和之和。

分析

显然选出的这前 kk 大的区间的区间和肯定是单调不升的,我们可以二分最小的区间和 midmid,看区间和不小于 midmid 的区间是否超过 kk 个。

要求区间和,显然可以直接用前缀和, check\text{check} 函数就是求 sumisumjmidsum_i-sum_{j}\ge midj+Lij+Rj+L\le i\le j+R(i,j)(i,j) 的个数是否大于等于 kk

移项一下可以得到 sumimid+sumjsum_i\ge mid+sum_{j},对于每个 jj,我们求出有多少个 ii 满足要求即可。

如果直接从 1n1\sim n 枚举 jj 的话,很难用什么数据结构在同时满足两个条件的情况下计数,但是我们可以改变一下枚举顺序,按 sumjsum_j 从大到小枚举,这样的话到下一个 sumjsum_{j'} 时满足第一个条件的集合需要新加入的 ii 也是从大到小的,加入时把第 ii 个位置标记为 11,统计 [j+L,j+R][j+L,j+R]11 的个数就是 jj 的答案。

为了改变枚举顺序,我们可以把 sumj=0n1sum_{j=0\sim n-1}sumi=1nsum_{i=1\sim n} 分别从大到小排序,用双指针 ++ 树状数组维护即可。

二分出最大的 midmid 后,用同样的办法统计符合条件的 sumisum_i,减去 sumjsum_j 就可以求到和,注意如果 (i,j)(i,j) 的个数大于 kk,要把多余的 midmid 减去。

令值域为 SS,则时间复杂度为 O(nlognlogS)O(n\log n\log S)由于这题的题号 2048 很二进制所以树状数组做法可以过

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
long long read(){
	long long x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
void write(long long x){
    if(x<0) putchar('-'),x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
const int N=5e5+10;
int n,L,R,ff;
ll a[N],s[N],l,r,c1[N],c2[N],ans2,k,cnt,w,z;
struct asdf{
	ll v;
	int id;
}d1[N],d2[N];
bool cmp(asdf x,asdf y){
	return x.v>y.v;
}
void add1(int x,ll v){
	for(;x<=n;x+=x&-x)
		c1[x]+=v;
}
ll ask1(int x){
	ll ans=0;
	if(x>n)
		x=n;
	for(;x;x-=x&-x)
		ans+=c1[x];
	return ans;
}
void add2(int x,ll v){
	for(;x<=n;x+=x&-x)
		c2[x]+=v;
}
ll ask2(int x){
	ll ans=0;
	if(x>n)
		x=n;
	for(;x;x-=x&-x)
		ans+=c2[x];
	return ans;
}
ll check(ll x,bool f){
	ll ans=0;
	for(int i=1;i<=n;i++)
		c1[i]=0;
	for(int i=1,j=1;i<=n;i++){
		while(j<=n&&d1[j].v>=d2[i].v+x){
			add1(d1[j].id,1);
			if(f)
				add2(d1[j].id,d1[j].v);
			j++;
		}
		w=ask1(d2[i].id+R)-ask1(d2[i].id+L-1);
		ans+=w;
		if(f)
			ans2+=ask2(d2[i].id+R)-ask2(d2[i].id+L-1)-d2[i].v*w;
	}
	return ans;
}
int main(){
	n=read();k=read();L=read();R=read();
	for(int i=1;i<=n;i++){
		a[i]=read();
		s[i]=s[i-1]+a[i];
		d1[i].v=s[i];
		d2[i].v=s[i-1];
		d1[i].id=i;d2[i].id=i-1;
		if(a[i]<0)
			l+=a[i];
		else
			r+=a[i];
	}
	sort(d1+1,d1+n+1,cmp);
	sort(d2+1,d2+n+1,cmp);
	l*=k;r*=k;
	while(l<r){
		ll mid=(l+r+1)>>1;
		if(check(mid,0)>=k)
			l=mid;
		else
			r=mid-1;
	}
	ll p=check(l,1);
	ans2-=(p-k)*l;
	write(ans2);
	return 0;
}
posted @ 2023-07-15 12:51  luckydrawbox  阅读(11)  评论(0)    收藏  举报  来源