20200229 SCOI模拟T3(线段树)

T3 「2017 山东一轮集训 Day2」Pair

「2017 山东一轮集训 Day2」Pair
题目描述
给出一个长度为 n n n 的数列 { a i } \{a_i\} {ai} 和一个长度为 m m m 的数列 { b i } \{b_i\} {bi},求 { a i } \{a_i\} {ai} 有多少个长度为 m m m 的连续子数列能与 { b i } \{b_i\} {bi} 匹配。

两个数列可以匹配,当且仅当存在一种方案,使两个数列中的数可以两两配对,两个数可以配对当且仅当它们的和不小于 h h h

输入格式
第一行三个数字 n , m , h n,m,h n,m,h
第二行有 m m m 个数字 b 1 , b 2 , . . . , b m b_1,b_2,...,b_m b1,b2,...,bm
第三行有 n n n 个数字 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an

输出格式
输出一个数字, { a i } \{a_i\} {ai} 有多少个长度为 m m m 的连续子数列能与 { b i } \{b_i\} {bi} 匹配。

样例
样例输入 1
5 2 10
5 3
1 8 5 5 7
样例输出 1
2
样例输入 2
2 2 6
2 3
3 4
样例输出 2
1
样例输入 3
4 2 10
5 5
9 3 8 9
样例输出 3
1

数据范围与提示
对于 100 % 100\% 100% 的数据, 1 ≤ m ≤ n ≤ 150000 1\le m\le n\le 150000 1mn150000 1 ≤ a i , b i , h ≤ 1 0 9 1\le a_i,b_i,h\le 10^9 1ai,bi,h109

思路:
转化题意,使 b b b数组的每一位变为 h − b i h-b_i hbi
此时 a i a_i ai b i b_i bi能配对即为 a i a_i ai大于 b i b_i bi
令变化后的 b b b数组升序排序为 b 1 , b 2 , . . . , b m b_1,b_2,...,b_m b1,b2,...,bm
统计 a a a子串中大于 b i b_i bi的个数,记为 s i s_i si,得到 s s s数组
匹配时贪心,将最小的 a i a_i ai与最小的 b i b_i bi匹配
a a a b b b能配对当且仅当对于每一位 s i ≤ i s_i\le i sii,即 s i − i ≤ 0 s_i-i\le 0 sii0
可以用线段树维护
O ( n ) O(n) O(n)枚举子串,每一次移动都删除一个数,添加一个数
发现每一次添加或删除都影响一段前缀,区间修改即可
上传统计答案维护最小值,当整个区间的最小值都大于等于零时,就匹配上了
数据只需要离散 b b b,在 b b b中找到小于等于 a i a_i ai的最大数, a i a_i ai对其前缀产生贡献

注意:
l o w e r _ b o u n d lower\_bound lower_bound是返回大于等于 i i i的最小数的位置
u p p e r _ b o u n d upper\_bound upper_bound是返回大于 i i i的最小数的位置
b b b中找 a a a时要找小于等于 a i a_i ai的最大数,应该用 u p p e r _ b o u n d upper\_bound upper_bound后再减一

代码:
(这里时将 b b b降序排序,维护后缀)

#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define re register
#define lch p<<1
#define rch p<<1|1

inline char ch(){
	static char buf[1<<21],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;
}

inline int in{
	int s=0,f=1;char x;
	for(x=ch();x<'0'||x>'9';x=ch())	if(x=='-')	f=-1;
	for( ;x>='0'&&x<='9';x=ch())	s=(s<<1)+(s<<3)+(x&15);
	return f==1?s:-s;
}

const int A=5e5+5;
int n,m,all;
int a[A],b[A],c[A],len;
int sum[A];
struct Tree{
	int l,r,min,tag;
}tree[4*A];
int ans=0;

inline void lsh(){
	sort(c+1,c+1+m);
	len=unique(c+1,c+1+m)-(c+1);
	for(re int i=1;i<=m;++i){
		b[i]=lower_bound(c+1,c+1+m,b[i])-c;
		sum[b[i]]++;
	}
	for(int i=len+1;i>0;i--)//这里len不加1会玄学出错
		sum[i]=sum[i+1]+sum[i];
	return;
}

inline void pushup(int p){
	tree[p].min=min(tree[lch].min,tree[rch].min);
	return;
}

inline void pushdown(int p){
	tree[lch].min+=tree[p].tag;
	tree[rch].min+=tree[p].tag;
	tree[lch].tag+=tree[p].tag;
	tree[rch].tag+=tree[p].tag;
	tree[p].tag=0;
	return;
}

inline void build(int p,int l,int r){
	tree[p].l=l,tree[p].r=r;
	if(l==r){
		tree[p].min=-sum[l];
		return;
	}
	int mid=(l+r)>>1;
	build(lch,l,mid),build(rch,mid+1,r);
	pushup(p);
	return;
}

inline void add(int p,int l,int r,int val){
	if(tree[p].l>=l&&tree[p].r<=r){
		tree[p].min+=val;
		tree[p].tag+=val;
		return;
	}
	pushdown(p);
	int mid=(tree[p].l+tree[p].r)>>1;
	if(l<=mid)	add(lch,l,r,val);
	if(r>=mid+1)	add(rch,l,r,val);
	pushup(p);
	return;
}

signed main(){
//	freopen("pair.in","r",stdin);
//	freopen("pair.out","w",stdout);
	n=in,m=in,all=in;
	for(re int i=1;i<=m;++i){
		b[i]=in;
		b[i]=c[i]=all-b[i];
	}
	lsh();
	for(re int i=1;i<=n;++i){
		a[i]=in;
		if(a[i]<c[1]){  
			a[i]=0;
			continue;
		}	
		if(a[i]>c[len]){
			a[i]=len;
			continue;
		}	
		if(a[i]>=c[1]&&a[i]<=c[len]){
			a[i]=upper_bound(c+1,c+1+len,a[i])-(c+1);
			continue;
		}	
	}
	build(1,1,len);
	for(re int i=1;i<m;++i)
		if(a[i]!=0)	add(1,1,a[i],1);
	for(re int i=m;i<=n;++i){
		if(a[i-m]!=0)	add(1,1,a[i-m],-1);
		if(a[i]!=0)	add(1,1,a[i],1);
		if(tree[1].min>=0)	ans++;
	}
	printf("%d\n",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值