CFgym102394A - Artful Paintings(差分约束+二分)

题目

source

思路

在队友帮助下,理清了思路。
设每个点为\(x_i\),它们的取值为0或1。设前缀和为\(S_i\)
题目具有单调性,涂色越多越能符合条件。因此可以考虑二分。假设二分值为mid,那么约束条件如下:

  • \(S_r-S_{l-1} \ge K_i\)
  • \(mid - (S_r-S_{l-1}) \ge K_j\)
  • \(S_i-S_{i-1} \le 1\)
  • \(S_i-S_{i-1} \ge 0\)
  • \(S_n-S_0 \le mid\)
  • \(S_n-S_0 \ge mid\)

一定要注意把约束条件列全。
由于\(S_0\)恒等于0,所以令dist[0]=0,从0点开始跑判负环。直接跑可能会超时,如果某时刻在spfa中有dist[i]<0,说明一定有负环,因为图是双向联通。

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll maxn = 1e4 + 10;
ll vis[maxn];
bool used[maxn];
vector<pair<ll, ll> > g[maxn];
ll dist[maxn];
bool inQue[maxn];
queue<ll> que;
ll n, m1, m2;

void init(ll n) {
    for(ll i = 0; i <= n; i++) {
        g[i].clear();
        dist[i] = INF;
        vis[i] = 0;
        used[i] = 0;
        inQue[i] = 0;
    }
}

bool spfa(ll src) {
    dist[src] = 0;
    while (!que.empty()) que.pop();
    que.push(src);
    inQue[src] = true;
    while (!que.empty()) {
        ll u = que.front();
        used[u] = 1;
        que.pop();
        for (ll i = 0; i < g[u].size(); i++) {
            if (dist[u] + g[u][i].second < dist[g[u][i].first]) {
                dist[g[u][i].first] = dist[u] + g[u][i].second;
                if(dist[g[u][i].first] < 0) return false; // 剪枝
                if (!inQue[g[u][i].first]) {
                    inQue[g[u][i].first] = true;
                    vis[g[u][i].first]++;
                    if(vis[g[u][i].first] >= n + 1) return false;
                    que.push(g[u][i].first);
                }
            }
        }
        inQue[u] = false;
    }
    return true;
}


struct edge {
    ll l, r, w;
};
vector<edge> ed;
void build(ll n, ll mid) {
    for(ll i = 0; i < m1; i++) {
        ll l = ed[i].l, r = ed[i].r, w = ed[i].w;
        g[r].push_back({l - 1, -w});
    }
    for(ll i = m1; i < m1 + m2; i++) {
        ll l = ed[i].l, r = ed[i].r, w = ed[i].w;
        g[l - 1].push_back({r, mid - w});
    }
    for(ll i = 1; i <= n; i++) {
        g[i].push_back({i - 1, 0});
    }
    for(ll i = 1; i <= n; i++) {
        g[i - 1].push_back({i, 1});
    }
    g[n].push_back({0, -mid});
    g[0].push_back({n, mid});

}

int main() {
    ll t;
    cin >> t;
    while(t--) {
        ed.clear();
        cin >> n >> m1 >> m2;
        for(ll i = 0; i < m1 + m2; i++) {
            ll l, r, w;
            cin >> l >> r >> w;
            ed.push_back({l, r, w});
        }
        ll l = 0, r = n;
        while(l <= r) {
            ll mid = (l + r) / 2;
            init(n + 1);
            build(n, mid);
            if(spfa(0)) {
                r = mid - 1;
            } else {
                l = mid + 1;
            }
        }
        cout << l << endl;
    }
}

posted @ 2021-05-21 11:13  limil  阅读(102)  评论(0编辑  收藏  举报