8.9 线段树板子+三分补题+三维的bfs

  • 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 });
		//	cout<<"xx: "<<xx<<' '<<"yy: "<<yy<<' '<<"cc:"<<cc<<' '<<"dltea: "<<delta<<"step: "<<	step[xx][yy][cc]<<'\n';
		
		}
	}
	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;
}
可以拿这个样例去了解过程        1
        5 500
        OCOOO
        S...X
        OOOOO
        OOOOO
        OOOOO
posted @ 2024-08-09 18:32  冬天的睡袋  阅读(19)  评论(0编辑  收藏  举报