zoukankan      html  css  js  c++  java
  • 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^2sqrt{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; 
    }
    

      

  • 相关阅读:
    vue 使用 <iframe> 嵌入网页 地址实现动态配置
    vue 视频播放 vue-video-player
    vue 实现自定义序号, 并且翻页序号累加。
    关于 vue 使用 Handsontable 表格数据导出
    node.js Stream流的使用
    手把手教如何搭建node+egg项目 引入Sequelize
    实现 通过数据库里一个字段值相等 则把 他合为一条数据
    最近在项目中碰到把对象数组转为键值对,
    js 的数组怎么push一个对象. Js数组的操作push,pop,shift,unshift JavaScrip
    for循环
  • 原文地址:https://www.cnblogs.com/xugangfan/p/15211229.html
Copyright © 2011-2022 走看看