loj#2740. 「JOISC 2016 Day 4」最差记者 2

为啥网上这题很少有题解啊。

首先,改 A 和改 C 是一样的,我们考虑只改 C。让 D 去匹配 B。

有一个比较好想的做法,先考虑能够同国匹配的就先匹配掉(这个可以用 multiset),剩余的,考虑按值域来(实际上离散化后就是跟按序列一样),对于 $D_i$,那么所对应 $B_j$ 的值域在 $[D_i,max]$。

那么,分成 2 个部分,将 D 部的 $i$ 连向 B 部的 $[D_i,max]$ 这些点。那么,我们只需要看看有没有完美匹配即可。

假如同国匹配的全都匹配掉之后再去看看有没有完美匹配,则可能不行。

我们要让最后一定有完美匹配,所以对于同国能匹配的,还要判断删去 D 部这个点,还能不能形成完美匹配。

但这样复杂度是 $O(n^2\sqrt{n})$ 级别的。

考虑只需要判断有没有,想到 hall 定理。

即 $|could(S)|-|S|\ge 0$。

按照套路维护前缀 $|could(S)|,|S|$。对于 $D_i$,那么会对 $|S|$ 有贡献(我们是让 D 部匹配 B 部嘛)贡献范围是 $[D_i,max]$ (前缀集合大小贡献),对于 $B_i$,对 $|could(S)|$ 有贡献,贡献范围是 $[B_i,max]$,即对于 $D_j \in [B_i,max]$,才可能匹配它。

这个样子相当于 $+-1$,用线段树维护前缀最小值即可。(注意是前缀,因为要表示子集!!!为什么可以看另一道相似的题目的题解

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#define ll long long

using namespace std;
int rd() {
	int f=1,sum=0; char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)) {sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
	return sum*f;
}
ll lrd() {
	ll f=1,sum=0; char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)) {sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
	return sum*f;
}
/*
考虑改A和改C没区别
让结束的这一部去匹配之前的那一部
且维护线段树前缀和,这时对于点数就不一定是 l 了
可以考虑动态加 
*/ 
#define ls (cur<<1)
#define rs (ls|1)
#define int ll
const int N=(int)(5e5+5);
multiset<int>s[N];
multiset<int>::iterator it;
int mi[N<<2],tag[N<<2],A[N],B[N],C[N],D[N],lsh[N<<1],tot,n;

void push_up(int cur) {
	mi[cur]=min(mi[ls],mi[rs]);
}

void push_down(int cur) {
	if(!tag[cur]) return ;
	tag[ls]+=tag[cur]; tag[rs]+=tag[cur];
	mi[ls]+=tag[cur]; mi[rs]+=tag[cur];
	tag[cur]=0; 
}

void update(int cur,int l,int r,int cl,int cr,int v) {
	if(cl<=l&&r<=cr) return mi[cur]+=v,tag[cur]+=v,void();
	push_down(cur); 
	int mid=(l+r)>>1;
	if(cl<=mid) update(ls,l,mid,cl,cr,v);
	if(cr>mid) update(rs,mid+1,r,cl,cr,v);
	push_up(cur);
}

signed main() {
	n=rd();
	for(int i=1;i<=n;i++) A[i]=rd(),B[i]=rd(),lsh[++tot]=B[i];
	for(int i=1;i<=n;i++) C[i]=rd(),D[i]=rd(),lsh[++tot]=D[i];
	sort(lsh+1,lsh+1+tot); tot=unique(lsh+1,lsh+1+tot)-lsh-1;
	for(int i=1;i<=n;i++) {
		B[i]=lower_bound(lsh+1,lsh+1+tot,B[i])-lsh; D[i]=lower_bound(lsh+1,lsh+1+tot,D[i])-lsh;
		s[A[i]].insert(B[i]);
		update(1,1,tot,B[i],tot,1); //匹配的点数,只能是之后的 
		update(1,1,tot,D[i],tot,-1); //前缀的集合+1 
	}
	int ans=0;
	for(int i=n;i>=1;i--) {
		it=s[C[i]].upper_bound(D[i]);
		if(it==s[C[i]].begin()) ++ans;
		else {
			--it; int qwq=*it;
			s[C[i]].erase(it);
			update(1,1,tot,qwq,tot,-1); update(1,1,tot,D[i],tot,1);
			if(mi[1]<0) { //剩下的没有完美匹配 
				s[C[i]].insert(qwq); ++ans;
				update(1,1,tot,qwq,tot,1); update(1,1,tot,D[i],tot,-1);
			}
		}
	}
	printf("%lld",ans); return 0; 
}

  

posted @ 2021-08-31 17:03  FxorG  阅读(239)  评论(0编辑  收藏  举报