[XXI Open Cup.Grand Prix of Korea]Advertisement Matching

Advertisement Matching

题解

首先,题目的 a , b a,b a,b匹配显然是一个很常见的贪心。
有结论,设 a a a的集合为 A A A,如果 ∀ S ⊆ A , ∑ a i ∈ S a i ⩽ ∑ i = 1 m min ⁡ ( b i , ∣ S ∣ ) \forall S\subseteq A,\sum_{a_{i}\in S}a_{i}\leqslant \sum_{i=1}^{m}\min(b_{i},|S|) SA,aiSaii=1mmin(bi,S),那么一定存在一种匹配的方法,使得该题的要求成立。
这也就是Gale–Ryser theorem,这里有一种通过网络流的证明,我个人是通过类贪心的思想理解的。

证明:
该结论的必要性是显然的,我们只需要证明其的充分性,也就是满足上面结论时一定有解。
具体来说,由于对于相同的 ∣ S ∣ |S| S,后面的 ∑ i = 1 m min ⁡ ( b i , ∣ S ∣ ) \sum_{i=1}^{m}\min(b_{i},|S|) i=1mmin(bi,S)的值肯定是一定的。
而前面 ∑ a i ∈ S a i \sum_{a_{i}\in S}a_{i} aiSai的最大时肯定是选择 A A A集合中最大的几个数,我们只需要让这些数的和小于我们后面的值就行了。
如果该集合中的最小值,该式不成立,那显然是不行的,我们不妨假定我们去掉最小值时是成立的,现在要将这个最小值加进去。
我们可以将原来的图形根据 min ⁡ ( b i , ∣ S ∣ ) \min(b_{i},|S|) min(bi,S)想象成柱状图,我们现在新加入一个数,肯定有几个柱子会被拔高,而我们的限制是一个柱子最多只能选择一个位置染色。
如果最开始的柱状图就空出一行及以上,如果新加入的 a i ⩽ m a_{i}\leqslant m aim,显然是有位置的,如果 a i > m a_{i}> m ai>m,之前的肯定也比 m m m大,那之前就判无解了。
否则,如果没有空出一行以上,而我们让延伸的延伸,此时的空位个数肯定不会小于 a i a_{i} ai,如果,能填满就填,不能填满,肯定是有新延伸的空位原来也是空位。
注意,现在还在延伸的位置,之前肯定也是在延伸的,故之前必然是有一个 a i a_{i} ai覆盖了没有延伸的位置,却没有覆盖新延伸的位置,我们可以让它覆盖的位置改变一下,将覆盖没有延伸位置的地方改成覆盖延伸了的为的地方。
由于之前的 a a a都不小于 a i a_{i} ai,所以我们一定可以通过这种方法调整到让 a i a_{i} ai有位置可填。
这样就可以证明该式的充分性了。

证明了上面的结论,我们自然也就想到了如何维护原题是否可行的方法。
我们可以先将 a a a值排序,保证我们选择的 S S S大小固定时,我们所选择的 a a a的和是最大的。
此时,如果 ∀ k ∈ [ 1 , n ] , ∑ i = 1 m min ⁡ ( b i , k ) − ∑ i = 1 k a i ⩾ 0 \forall k\in[1,n],\sum_{i=1}^{m}\min(b_{i},k)-\sum_{i=1}^{k}a_{i}\geqslant 0 k[1,n],i=1mmin(bi,k)i=1kai0成立,那么我们是一定有解的,否则一定无解。
我们显然可以通过线段树维护不同的 k k k的值,如果 min ⁡ k = 1 n ( ∑ i = 1 m min ⁡ ( b i , k ) − ∑ i = 1 k a i ) ⩾ 0 \min_{k=1}^{n}\left(\sum_{i=1}^{m}\min(b_{i},k)-\sum_{i=1}^{k}a_{i}\right)\geqslant 0 mink=1n(i=1mmin(bi,k)i=1kai)0,就有解。
如果 b i + 1 b_{i}+1 bi+1,显然会让 k ⩾ b i + 1 k\geqslant b_{i}+1 kbi+1的部分,上式的值 + 1 +1 +1
b i − 1 b_{i}-1 bi1则会让 k ⩾ b i k\geqslant b_{i} kbi的部分,上式 − 1 -1 1
由于我们 b b b的加值是取的 min ⁡ ( b i , k ) \min(b_{i},k) min(bi,k),这是显然的。
a i a_{i} ai的改变则涉及到了该元素的 r k rk rk,记 r k x rk_{x} rkx表示大于等于 x x x的数的个数。
如果 a i + 1 a_{i}+1 ai+1,那么 k ⩾ r k a i + 1 + 1 k\geqslant rk_{a_{i}+1}+1 krkai+1+1的部分,上式值会 + 1 +1 +1,再让 r k a i + 1 + + rk_{a_{i}+1}++ rkai+1++
a i − 1 a_{i}-1 ai1,会让 k ⩾ r k a i k\geqslant rk_{a_{i}} krkai的部分值 − 1 -1 1,再让 r k a i − − rk_{a_{i}}-- rkai
以上的四种操作都是线段树上区间的修改,我们只用看修改完的的最小值就行了。

时间复杂度 O ( n + m + q log ⁡   n ) O\left(n+m+q\log\,n\right) O(n+m+qlogn)

源码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 250005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long LL;
//#define int LL
typedef unsigned long long uLL;     
const LL INF=0x3f3f3f3f3f3f3f3f;  
const int mo=998244353;
const int inv2=499122177;
const int jzm=2333;
const int n1=50;
const int lim=250000;
const int zero=10000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-5;
typedef pair<LL,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){putchar('\n');while(x>9){putchar((x%10)|'0');x/=10;}putchar(x|'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int n,m,q,a[MAXN],b[MAXN],c[MAXN],d[MAXN],rk[MAXN];
LL dif[MAXN],val[MAXN];
class SegmentTree{
	private:
		LL minn[MAXN<<2],lzy[MAXN<<2];
		//void pushup(int rt){minn[rt]=min(minn[lson],minn[rson]);}
		#define pushup(rt) minn[rt]=min(minn[lson],minn[rson])
		void pushdown(int rt){
			if(lzy[rt]){
				minn[lson]+=lzy[rt];lzy[lson]+=lzy[rt];
				minn[rson]+=lzy[rt];lzy[rson]+=lzy[rt];
				lzy[rt]=0;
			}
		}
	public:
		void build(int rt,int l,int r){
			if(l==r){minn[rt]=val[l];return ;}int mid=l+r>>1;
			build(lson,l,mid);build(rson,mid+1,r);pushup(rt);
		}
		void modify(int rt,int l,int r,int al,int ar,LL aw){
			if(l>r||l>ar||r<al||al>ar)return ;
			if(al<=l&&r<=ar){minn[rt]+=aw;lzy[rt]+=aw;return ;}
			int mid=l+r>>1;pushdown(rt);
			if(al<=mid)modify(lson,l,mid,al,ar,aw);
			if(ar>mid)modify(rson,mid+1,r,al,ar,aw);
			pushup(rt);
		}
		void search(int rt,int l,int r){
			if(l==r){printf("%d ",minn[rt]);return ;}
			int mid=l+r>>1;pushdown(rt);
			search(lson,l,mid);search(rson,mid+1,r);
		}
		LL ask(){return minn[1];} 
		bool query(){return minn[1]>=0;}
}T;
signed main(){
	read(n);read(m);LL summ=0;
	for(int i=1;i<=n;i++)read(a[i]),c[i]=-a[i],rk[a[i]]++;
	for(int i=1;i<=m;i++)read(b[i]),d[i]=b[i];
	sort(c+1,c+n+1);sort(d+1,d+m+1);
	for(int i=lim;i>0;i--)rk[i]+=rk[i+1];
	for(int i=1,j=1;i<=n;i++){
		while(j<=m&&d[j]<=i)summ+=1ll*d[j],j++;
		summ+=1ll*c[i];val[i]=summ+1ll*i*(m-j+1);
	}
	T.build(1,1,n);read(q);
	for(int i=1;i<=q;i++){
		int opt,x;read(opt);read(x);
		if(opt==1)a[x]++,rk[a[x]]++,T.modify(1,1,n,rk[a[x]],n,-1);
		if(opt==2)T.modify(1,1,n,rk[a[x]],n,1),rk[a[x]]--,a[x]--;
		if(opt==3)b[x]++,T.modify(1,1,n,b[x],n,1);
		if(opt==4)T.modify(1,1,n,b[x],n,-1),b[x]--;
		printf("%d\n",T.query());
	}
	return 0;
}

谢谢!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值