LOJ6062「2017 山东一轮集训 Day2」Pair(Hall定理,线段树)

题面

给出一个长度为 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

1 ≤ m ≤ n ≤ 150000. 1\leq m\leq n\leq 150000. 1mn150000.

题解

条件等价于子二分图存在完备匹配。

我们从数列 B B B 的角度考虑,根据 Hall 定理,要保证 B B B 的任意子集 S S S 满足 ∣ S ∣ ≤ ∣ N ( S ) ∣ |S|\leq |N(S)| SN(S) N ( S ) N(S) N(S) 表示 S S S 中所有点的邻接点构成的集合。

然后,我们会发现连边具有单调性, a i a_i ai 可以和所有大于等于 h − a i h-a_i hai b j b_j bj 连边。所以,我们把 B B B 从小到大排序,每个 a i a_i ai 的邻接点集就是 B B B 的某段后缀。

这就意味着,对于 ∀ i < j ≤ m   ,   N ( i ) ⊆ N ( j ) \forall i<j\leq m~,~N(i)\sube N(j) i<jm , N(i)N(j) ,即 i i i 的邻接点集一定是 j j j 的邻接点集的子集。很容易就能得到,如果 B B B 的某前缀 P P P 满足 ∣ P ∣ ≤ ∣ N ( P ) ∣ |P|\leq|N(P)| PN(P) ,那么对于任意 S ⊆ B , ∣ S ∣ = ∣ P ∣ S\sube B,|S|=|P| SB,S=P ,也一定满足 ∣ S ∣ ≤ ∣ N ( S ) ∣ |S|\leq|N(S)| SN(S)

于是,我们需要考虑的子集从 2 m − 1 2^m-1 2m1 个锐减至 m m m 个。而且,明显可以用线段树维护每个前缀的邻接点集大小,存在完备匹配的条件即 min ⁡ { ∣ N ( P ) ∣ − ∣ P ∣ } ≥ 0 \min\{|N(P)|-|P|\}\geq 0 min{N(P)P}0

时间复杂度 O ( n log ⁡ m ) O(n\log m) O(nlogm)

CODE

#include<set>
#include<map>
#include<cmath>
#include<stack>
#include<queue>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 150005
#define LL long long
#define DB double
#define ENDL putchar('\n')
#define lowbit(x) (-(x) & (x))
LL read() {
	LL f=1,x=0;int s = getchar();
	while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s=getchar();}
	while(s >= '0' && s <= '9') {x = (x<<3) + (x<<1) + (s^48);s = getchar();}
	return f * x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);};
void putnum(LL x) {
	if(!x) {putchar('0');return ;}
	if(x<0) putchar('-'),x = -x;
	return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}

int n,m,s,o,k;
int H;
int a[MAXN],b[MAXN],ad[MAXN];
int tre[MAXN<<2],lz[MAXN<<2],M;
void maketree(int n) {
	M=1;while(M<n+2)M<<=1;
	for(int i = 1;i <= n;i ++) {
		tre[M+i] = -i;
	}
	for(int i = M-1;i > 0;i --) {
		tre[i] = min(tre[i<<1],tre[i<<1|1]);
	}return ;
}
void addtree(int l,int r,int y) {
	if(l > r) return ;
	for(int s=M+l-1,t=M+r+1;s || t;s >>= 1,t >>= 1) {
		if(s<M) tre[s] = min(tre[s<<1],tre[s<<1|1]) + lz[s];
		if(t<M) tre[t] = min(tre[t<<1],tre[t<<1|1]) + lz[t];
		if((s>>1) ^ (t>>1)) {
			if(!(s&1)) tre[s^1] += y,lz[s^1] += y;
			if(t & 1) tre[t^1] += y,lz[t^1] += y;
		}
	}return ;
}
int main() {
	n = read();m = read();H = read();
	for(int i = 1;i <= m;i ++) {
		b[i] = read();
	}
	for(int i = 1;i <= n;i ++) {
		a[i] = read();
	}
	sort(b + 1,b + 1 + m);
	maketree(m);
	int ans = 0;
	for(int i = 1;i <= n;i ++) {
		ad[i] = lower_bound(b + 1,b + 1 + m,H-a[i]) - b;
		addtree(ad[i],m,1);
		if(i > m) addtree(ad[i-m],m,-1);
		if(tre[1] >= 0) ans ++;
	}
	AIput(ans,'\n');
	return 0;
}
posted @ 2021-10-28 17:08  DD_XYX  阅读(37)  评论(0编辑  收藏  举报