21.7.7 t3

tag:贪心,扫描线,二分图匹配


结果是最水的一道

首先要想到一个贪心结论:一定是两两匹配,若干个形如 \(x\to y\to x\) 的环。

然后可以二分图匹配。

观察匹配的条件:

  • \(a_i\ge j\)
  • \(i\le b_j\)

如果用二维点表示为 \((a_i,i)\)\((j,b_j)\),那么可以看作是一个 \((j,b_j)\) 的点可以匹配右下角所有 \((a_i,i)\)

然后就是另一个贪心,先扫描线从右往左,然后贪心匹配范围内最上面的一个。

复杂度 \(O(nlogn)\)


#include<bits/stdc++.h>
using namespace std;
 
template<typename T>
inline void Read(T &n){
    char ch; bool flag=false;
    while(!isdigit(ch=getchar())) if(ch=='-')flag=true;
    for(n=ch^48; isdigit(ch=getchar()); n=(n<<1)+(n<<3)+(ch^48));
    if(flag) n=-n;
}
#define no !
 
typedef long long ll;
enum{
    MAXN = 500005
};
 
struct point{
    int x, y, opt;
    inline bool operator <(const point &k)const{
        if(x!=k.x) return x>k.x;
        if(y!=k.y) return y<k.y;
        return opt>k.opt;
    }
}p[MAXN<<1];
 
int n, m;
int a[MAXN], b[MAXN], c[MAXN], d[MAXN];
ll ans;
 
typedef pair<int,int> pii;
set<pii>st;
set<pii>::iterator it;
 
int main(){
    Read(n); Read(m);
    for(int i=1; i<=n; i++) Read(a[i]);
    for(int i=1; i<=m; i++) Read(b[i]);
    for(int i=1; i<=n; i++) Read(c[i]);
    for(int i=1; i<=m; i++) Read(d[i]);
    for(int i=1; i<=n; i++) p[i].x = a[i], p[i].y = i, p[i].opt = i;
    for(int i=1; i<=m; i++) p[i+n].x = i, p[i+n].y = b[i], p[i+n].opt = -i;
    n += m; sort(p+1,p+n+1);
    for(int i=1; i<=n; i++){
        if(p[i].opt>0) st.insert(pii(-p[i].y,p[i].opt));
        else{
            int &rem = d[-p[i].opt];
            while(rem and !st.empty()){
                it = st.lower_bound(pii(-p[i].y,0));
                if(it==st.end()) break;
                if(c[it->second] <= rem) rem -= c[it->second], ans += c[it->second], c[it->second] = 0, st.erase(it);
                else c[it->second] -= rem, ans += rem, rem = 0;
            }
        }
    }
    cout<<ans<<'\n';
    return 0;
}
 
/*
3 3
3 1 2
1 2 3
1 1 1
1 1 1
*/
posted @ 2021-07-07 21:11  oisdoaiu  阅读(42)  评论(0编辑  收藏  举报