LOJ#6062. 「2017 山东一轮集训 Day2」Pair 题解

题目链接

对每个数 \(a_i\) 求出它能匹配的 \(b_j\) 的数目,记为 \(c_i\)

考虑 Hall 定理,对于 \(c_i\) 的一段长度为 \(m\) 的连续子段,记 \(s_i\)\(\leq i\)\(c_i\) 的数目,因为 Hall 定理所以有 \(s_i \leq i\) , 并且如果满足 \(s_i \leq i\) 那么一定有完美匹配。

那么直接线段树上维护 \(s_i - i\)\(\min\) 即可。

\(\Theta (n\log m)\)

code :

#include <bits/stdc++.h>
using namespace std;
template <typename T> void read(T &x){
	static char ch; x = 0,ch = getchar();
	while (!isdigit(ch)) ch = getchar();
	while (isdigit(ch)) x = x * 10 + ch - '0',ch = getchar();
}
inline void write(int x){if (x > 9) write(x/10); putchar(x%10+'0'); }
const int N = 150005;
int n,m,h,a[N],b[N];
struct Seg{
	#define lc o<<1
	#define rc o<<1|1
	int val[N<<2],tag[N<<2];
	inline void up(int o){ val[o] = min(val[lc],val[rc]); }
	inline void Tag(int o,int vv){ tag[o] += vv,val[o] += vv; }
	inline void down(int o){ if (tag[o]) Tag(lc,tag[o]),Tag(rc,tag[o]),tag[o] = 0; }
	inline void Build(int o,int l,int r){
		if (l == r){ val[o] = l; return; }
		int mid = l+r>>1; Build(lc,l,mid),Build(rc,mid+1,r),up(o);
	}
	int ll,vv;
	inline void Add(int o,int l,int r){
		if (ll <= l){ Tag(o,vv); return; }
		down(o); int mid = l+r>>1; if (ll <= mid) Add(lc,l,mid); Add(rc,mid+1,r); up(o);
	}
	inline void add(int p,int x){ ll = p,vv = x,Add(1,0,m); }
	#undef lc
	#undef rc
}T;
int main(){
	int i,ans = 0;
	read(n),read(m),read(h);
	for (i = 1; i <= m; ++i) read(b[i]); sort(b+1,b+m+1);
	for (i = 1; i <= n; ++i) read(a[i]),a[i] = m - (lower_bound(b+1,b+m+1,h-a[i]) - b - 1);
	T.Build(1,0,m);
	for (i = 1; i <= n; ++i){
		T.add(a[i],-1);
		if (i >= m){
			if (i > m) T.add(a[i-m],1);
			if (T.val[1] >= 0) ++ans;
		}
	}
	write(ans),putchar('\n');
	return 0;
}
posted @ 2020-09-28 17:31  srf  阅读(220)  评论(0编辑  收藏  举报