8.9 線段樹板子+三分補題+三維的bfs

冬天的睡袋發表於2024-08-09
  • nowcoder訓練
    區間
    線段樹板子題,我們只需要把區間每一個點設定成1,然後修改的時候直接改點,然後查區間就行
    線段樹維護最大欄位和/ 01 串最大連續 1 的個數模板題。

把白色和黑色看成 1/0 兩個數就行了。

#include <bits/stdc++.h>
 
using namespace std;
 
using i64 = long long;
 
#define lc u << 1
#define rc u << 1 | 1
const int N = 1e5 + 5;
i64 w[N], n, m, p;
struct Tree { //線段樹
    int l, r;
    int sum1, lmax1, rmax1, max1;
    int sum0, lmax0, rmax0, max0;
    int tag, rev;
} tr[N << 2];
 
void cal_lazy(int fa, int ch) {
    int len = tr[ch].r - tr[ch].l + 1;
    if (tr[fa].tag == 0) {
        tr[ch].tag = 0, tr[ch].rev = 0;
        tr[ch].sum0 = tr[ch].lmax0 = tr[ch].rmax0 = tr[ch].max0 = len;
        tr[ch].sum1 = tr[ch].lmax1 = tr[ch].rmax1 = tr[ch].max1 = 0;
    }
    if (tr[fa].tag == 1) {
        tr[ch].tag = 1, tr[ch].rev = 0;
        tr[ch].sum0 = tr[ch].lmax0 = tr[ch].rmax0 = tr[ch].max0 = 0;
        tr[ch].sum1 = tr[ch].lmax1 = tr[ch].rmax1 = tr[ch].max1 = len;
    }
    if (tr[fa].rev) {
        tr[ch].rev ^= 1;
        swap(tr[ch].sum1, tr[ch].sum0);
        swap(tr[ch].lmax1, tr[ch].lmax0);
        swap(tr[ch].rmax1, tr[ch].rmax0);
        swap(tr[ch].max1, tr[ch].max0);
    }
}
 
void tag_union(int fa, int ch) {
    tr[ch].tag = tr[fa].tag;
    tr[ch].rev ^= tr[fa].rev;
}
 
void init_lazy(int u) {
    tr[u].tag = -1;
    tr[u].rev = 0;
}
 
void pushdown(int u) {
    if (tr[u].tag != -1 || tr[u].rev != 0) {
        cal_lazy(u, lc);
        cal_lazy(u, rc);
        // tag_union(u, lc);
        // tag_union(u, rc);
        init_lazy(u);
    }
}
 
void pushup(int u) { //上傳
    tr[u].sum1 = tr[lc].sum1 + tr[rc].sum1;
    tr[u].sum0 = tr[lc].sum0 + tr[rc].sum0;
    tr[u].lmax1 = tr[lc].lmax1 + (tr[lc].sum0 ? 0 : tr[rc].lmax1);
    tr[u].rmax1 = tr[rc].rmax1 + (tr[rc].sum0 ? 0 : tr[lc].rmax1);
    tr[u].lmax0 = tr[lc].lmax0 + (tr[lc].sum1 ? 0 : tr[rc].lmax0);
    tr[u].rmax0 = tr[rc].rmax0 + (tr[rc].sum1 ? 0 : tr[lc].rmax0);
    tr[u].max1 = max({tr[lc].max1, tr[rc].max1, tr[lc].rmax1 + tr[rc].lmax1});
    tr[u].max0 = max({tr[lc].max0, tr[rc].max0, tr[lc].rmax0 + tr[rc].lmax0});
}
 
void build(int u, int l, int r) { //建樹
    tr[u].l = l, tr[u].r = r;
    init_lazy(u);
    if (l == r) {
        int t = 1;
        tr[u] = {l, r, t, t, t, t, t ^ 1, t ^ 1, t ^ 1, t ^ 1, -1, 0};
        return ;
    }
    int mid = (l + r) >> 1;
    build(lc, l, mid);
    build(rc, mid + 1, r);
    pushup(u);
 
}
 
void modify(int u, int l, int r, int op) {
    if (tr[u].l >= l && tr[u].r <= r) {
        int len = tr[u].r - tr[u].l + 1;
        if (op == 0) {
            tr[u].rev = 0, tr[u].tag = 0;
            tr[u].sum0 = tr[u].lmax0 = tr[u].rmax0 = tr[u].max0 = len;
            tr[u].sum1 = tr[u].lmax1 = tr[u].rmax1 = tr[u].max1 = 0;
        } else if (op == 1) {
            tr[u].rev = 0, tr[u].tag = 1;
            tr[u].sum0 = tr[u].lmax0 = tr[u].rmax0 = tr[u].max0 = 0;
            tr[u].sum1 = tr[u].lmax1 = tr[u].rmax1 = tr[u].max1 = len;
        } else {
            tr[u].rev ^= 1;
            swap(tr[u].sum1, tr[u].sum0);
            swap(tr[u].lmax1, tr[u].lmax0);
            swap(tr[u].rmax1, tr[u].rmax0);
            swap(tr[u].max1, tr[u].max0);
        }
        return ;
    }
    pushdown(u);
    int mid = (tr[u].l + tr[u].r) >> 1;
    if (l <= mid)
        modify(lc, l, r, op);
    if (r > mid)
        modify(rc, l, r, op);
    pushup(u);
}
 
Tree query(int u, int l, int r) { //區查
    if (l <= tr[u].l && tr[u].r <= r)
        return tr[u];
    int mid = tr[u].l + tr[u].r >> 1;
    pushdown(u);
    if (r <= mid) return query(lc, l, r);
    else if (l > mid) return query(rc, l, r);
    else {
        Tree res, L = query(lc, l, mid), R = query(rc, mid + 1, r);
        res.sum1 = L.sum1 + R.sum1;
        res.sum0 = L.sum0 + R.sum0;
        res.lmax1 = L.lmax1 + (L.sum0 ? 0 : R.lmax1);
        res.rmax1 = R.rmax1 + (R.sum0 ? 0 : L.rmax1);
        res.lmax0 = L.lmax0 + (L.sum1 ? 0 : R.lmax0);
        res.rmax0 = R.rmax0 + (R.sum1 ? 0 : L.rmax0);
        res.max1 = max({L.max1, R.max1, L.rmax1 + R.lmax1});
        res.max0 = max({L.max0, R.max0, L.rmax0 + R.lmax0});
        return res;
    }
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
 
    int n,q;
    cin >> n >> q;
 
    build(1,1,n);
 
    while(q--){
        int op;
        cin >> op;
        if(op == 1){
            int x;
            cin >> x;
            modify(1,x,x,2);
        }else{
            int l,r;
            cin >> l >> r;
 
            auto ans = query(1,l,r);
            cout << ans.max1 << '\n';
        }
    }
    
    return 0;
}

G-求值
三分,化簡後只有兩個數,然後函式一定是v型的,我們列舉x然後三分y,每次更新最小值即可

#include <bits/stdc++.h>
 
using namespace std;
 
using i64 = long long;
 
void solve() {
 
    i64 a, b, c, n, w;
    cin >> a >> b >> c >> n >> w;
 
    auto check = [&](i64 mid, i64 x) {
        return abs(x * a + mid * b + (n - mid - x) * c - w);
    };
 
    i64 ans = LLONG_MAX >> 1;
    for (int i = 0; i <= n; i ++) {
 
        int l = 0, r = n - i;
        while (l <= r) {
            i64 midl = l + (r - l) / 3;
            i64 midr = r - (r - l) / 3;
            ans = min({ans, check(midl, i), check(midr, i)});
            if (check(midl, i) > check(midr, i))
                l = midl + 1;
            else
                r = midr - 1;
        }
    }
 
    cout << ans << '\n';
 
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
 
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
 
    return 0;
}

[wyh的吃雞(https://ac.nowcoder.com/acm/problem/15445)
這道題難在不能標記vis,因為有可能先沒有經過這個點,當車重新走過的時候車經過的時間會更短,所以這道題不能標記vis
但是我們可以這樣去做,開成三維,第三維表示用不用車經過有兩個狀態,不用車經過和用車,不用車經過很簡單就是想標記vis一樣,用車我們需要比較同一級的
一旦用車,後面的情況都是用車的,因為可能用車也存在快慢,不要車也由快慢,最後只剩下兩個狀態,即到終點有沒有用車,然後每次更新時間分成有沒有車去判斷;
最後我們只需要遍歷終點的有無車狀態有沒有解取min即可

#include <bits/stdc++.h>
#define ll long long
 
using namespace std;
 
int n, t;
char dt[107][107];
int step[107][107][2];
const int dir[4][2] = { {1,0},{-1,0},{0,1}, { 0,-1 } };
 
struct node {
    int x, y;
    bool c;
};
 
int bfs(node st, vector<node> &ed) {
    queue<node> q;
    q.push(st);
    step[st.x][st.y][0] = 0;
    while (!q.empty()) {
        node cur = q.front();
        q.pop();
        if (dt[cur.x][cur.y] == 'X') continue;
        for (int i = 0;i < 4;i++) {
            int xx = cur.x + dir[i][0];
            int yy = cur.y + dir[i][1];
            int delta = cur.c ? 1 : 2;
            if (xx < 0 || xx >= n || yy < 0 || yy >= n || dt[xx][yy] == 'O')continue;
            bool cc = cur.c || dt[xx][yy] == 'C';
            if (step[xx][yy][cc] <= step[cur.x][cur.y][cur.c] + delta) continue;
            ///不能用vis鎖定點,因為擴充套件時間線混亂,不按照時間順序擴充套件
            ///有可能時間晚的車先佔了格子,早的車沒擴充套件過來就無法覆蓋了,因此只能用距離覆蓋
            step[xx][yy][cc] = step[cur.x][cur.y][cur.c] + delta;
            q.push({ xx,yy,cc });
        }
    }
    int ans = 1e9;
    for (auto e : ed) ans = min({ ans, step[e.x][e.y][0],step[e.x][e.y][1] });///目的地是個連通塊
    return ans;
}
 
bool solve() {
    cin >> n >> t;
    node st;
    vector<node> ed;
    for (int i = 0;i < n;i++) {
        for (int j = 0;j < n;j++) {
            cin >> dt[i][j];
            if (dt[i][j] == 'S') st.x = i, st.y = j, st.c = 0;
            if (dt[i][j] == 'X') ed.push_back({ i,j,0 });
            step[i][j][0] = step[i][j][1] = 1e9;
        }
    }
    int ans = bfs(st, ed);
    if (ans > t) return false;
    else cout << "YES\n" << ans << '\n';
    return true;
}
 
int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) {
        if (!solve()) cout << "NO" << '\n';
    }
    return 0;
}