1. 程式人生 > 其它 >21.7.7 t3

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
*/