2022河南萌新联赛第(二)场-数对-(偏序问题)

数对

题目大意:

问数组满足条件的 l − r l-r lr对数。
a l + a l + 1 + a r − 1 + a r < = x + y ( r − l + 1 ) a_l+a_l+_1+a_r-_1+a_r<=x+y(r-l+1) al+al+1+ar1+ar<=x+y(rl+1)

思路:

预处理

查询哪个区间,区间每个数都减去 y y y,故原数组都减去 y y y
式子转变为
( a l − y ) + ( a l + 1 − y ) + ( a r − 1 − y ) + ( a r − y ) < = x (a_l-y)+(a_l+_1-y)+(a_r-_1-y)+(a_r-y)<=x (aly)+(al+1y)+(ar1y)+(ary)<=x
区间合查询处理一个前缀合。
再转化式子
s u m [ r ] < = x + s u m [ l − 1 ] sum[r]<=x+sum[l-1] sum[r]<=x+sum[l1]
r > = l , r > l − 1 r>=l,r>l-1 r>=l,r>l1

如何计数(偏序问题)

式子化简后有两个条件,这是一个偏序问题。
对于这种只有两个条件的偏序一般都是通过排序先满足一个条件,再通过数据结构计数。
如果按下标排序 s u m [ i ] sum[i] sum[i]本身比较大不好处理。
可以按 s u m [ i ] sum[i] sum[i]的值升序排序。
这样排序有一个好处
对于一个 s u m [ i ] sum[i] sum[i],需要把所有满足条件的( s u m [ ? ] < = x + s u m [ i ] sum[?]<=x+sum[i] sum[?]<=x+sum[i])都加入数据结构中去,再查询坐标比 i i i大的个数。如果每个 i i i操作都要回退,时间复杂度难以保证。
升序排序可以发现 x + s u m [ i ] x+sum[i] x+sum[i]是在一直变大的,加入数据结构的点只会变多,避免了回退操作。
查询比一个数或多或少实现的方法比较多。

很重要的一个点

处理的是前缀合,对于点 1 1 1,一定要把 s u m [ 0 ] = 0 sum[0]=0 sum[0]=0加入。
不加 d e b u g debug debug会很爽

Code

#include <iostream>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <algorithm>
#include <vector>
#include <string>
#include <iomanip>
#include <cmath>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <climits>
//#include <unordered_map>
#define guo312 std::ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define ll long long
#define Inf LONG_LONG_MAX
#define inf INT_MAX
#define endl "\n"
#define PI 3.1415926535898
using namespace std;
const int N=2e5+10;
ll n,x,y,a[N],sum[N];

ll c[N];
struct PW{
	ll val,id;
}b[N];

bool cmp(PW s1,PW s2){
	if(s1.val!=s2.val){
		return s1.val<s2.val;
	}
	else{
		return s1.id<s2.id;
	}
}

int lowbit(int x){
	return x&(-x);
}

void add(int x,int num){
    if(x==0) return ;
	for( ;x<=n+1;c[x]+=num,x+=lowbit(x));
}

ll query(int x){
	ll re=0;
	for( ;x>0;re+=c[x],x-=lowbit(x));
	return re;
}

int main(){
guo312;
	cin>>n>>x>>y; ll ans=0;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		a[i]-=y;
	}
	for(int i=1;i<=n;i++){
		sum[i]=sum[i-1]+a[i];
		b[i].id=i,b[i].val=sum[i];
	}
    b[n+1].id=0,b[n+1].val=0;
	sort(b+1,b+1+n+1,cmp); int now=1; 
	for(int i=1;i<=n+1;i++){
		ll val=b[i].val+x;
		while(now<=n&&b[now].val<=val){
			add(b[now].id,1);
			now++;
		}
		ans+=query(n)-query(b[i].id);
	}
	cout<<ans;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

要用bug来打败bug

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值