BZOJ

题意:
在二维网格图中有\(n\)个物品,每个物品有价值;但有\(m\)个警卫看管这些物品,每个警卫面朝\(y\)轴负方向,能看到一定角度(假定能够看到无穷远)。
现在每个敬畏有一个贿赂价钱,通过贿赂能让警卫闭眼。如果一个物品没被任何警卫看到,则可以取走它。
问最后获得的最大价值为多少。

思路:

  • 最直接的思路就是一个最大权闭合子图的建模,但边数为\(O(n^2)\),强行搞直接\(T\)飞。
  • 然后呢...因为最小割等于最大流,又因为这个是二分图,所以我们可以模拟最大流(想不到...)。
  • 但是我们需要知道每个警察看管着哪些物品,这里我们将警察视角强行变为\(90^o\)(旋转坐标系),然后将平面逆时针旋转\(90^o\),最终就得到了如下样子:
  • 那么问题就转化为:每个警卫能够给出\(b_i\)的流量,每个物品能接受\(a_i\)的流量,求最大流。
  • 直接按\(x\)从大到小考虑每个警卫,类似于扫描线,在这个过程中不断加入物品。对于每个警卫,显然优先考虑\(y\)较大的那些点是更优的。然后就模拟最大流过程即可。
  • 可以用\(set\)来维护信息,复杂度为\(O(nlogn)\)

PS:旋转坐标轴的时候其实只需要单独分析向量就行,另外有一个公式,假设现在有两个坐标系,一个为普通的直角坐标系,另一个是旋转过后的,它们的基底矩阵分别为\(E,A\),那么假设点\((x,y)\)为基底\(E\)下的点,点\((a,b)\)为基底\(A\)下的点。有公式:\((x,y)E=(a,b)A\),即这两个点在我们看来是同一个点,只是在不同的坐标系下有着不同的表示。知道这个就可以愉快地旋转坐标系了。
代码如下:

/*
 * Author:  heyuhhh
 * Created Time:  2019/10/30 21:16:50
 */
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
  #define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pii;
//head
const int N = 2e5 + 5;

int n, m, w, h;
struct Node{
    ll x, y, z;
    bool operator < (const Node &A) const{
        return x < A.x;
    }
}a[N], b[N];

void run(){
    cin >> w >> h;
    ll ans = 0;
    for(int i = 1; i <= n; i++) {
        ll x, y, z;
        cin >> x >> y >> z;
        ans += z;
        x = 1ll * x * h;
        y = 1ll * y * w;
        a[i] = Node{x + y, x - y, z};
    }
    for(int i = 1; i <= m; i++) {
        ll x, y, z;
        cin >> x >> y >> z;
        x = 1ll * x * h;
        y = 1ll * y * w;
        b[i] = Node{x + y, x - y, z};
    }
    sort(a + 1, a + n + 1);
    sort(b + 1, b + m + 1);
    set <pii> S;
    for(int i = 1, j = 1; i <= m; i++) {
        while(j <= n && a[j].x <= b[i].x) {
            S.insert(MP(a[j].y, a[j].z));
            ++j;
        }
        ll c = b[i].z;
        while(c) {
            set <pii> :: iterator it = S.lower_bound(MP(b[i].y, 0));
            if(it == S.end()) break;
            pii now = *it;
            S.erase(it);
            ll tmp = min(c, now.se);
            now.se -= tmp;
            c -= tmp;
            ans -= tmp;
            if(now.se > 0) S.insert(now);
        }
        dbg(ans);
    }
    cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    while(cin >> n >> m) run();
    return 0;
}
12-27 16:01