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