Loading

GZEZ "六校NOI集训" Day4

T1(magic)

网络流建模。
使用格点(网格的交点)进行连边。
将源点与左侧边界连边,右侧和下侧与汇点连边。
中间所有的可以用于封路的进行连边,如果是屏障,费用为 \(1\) , 否则为 \(0\)
并且提前处理小 \(Y\) 走的最优路径(即尽量贴着上边),标记,与这些路径冲突的边不连。
怪物生命的限制可将源点拆开,增加一个点权。
费用流即可,注意使用 \(SPFA\)


#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
#define ep(i, u, t) for (int i = H[u], t = e[i].v; i; i = e[i].n, t = e[i].v)
 
const int _ = 300 + 7;
const int inf = 1e7;
typedef long long ll;
typedef std::pair<int, int> PII;
 
struct edge { int v, n, c, w; }e[_ * _ * _]; int H[_ * _], cnte = 1;
int n, m, v, k, c[_][_], dis[_][_], x[_ * _], y[_ * _], o[_ * _]; bool nt[_][_], vis[_ * _];
int pre[_][_], ans;
int dep[_ * _], S, T, U, fro[_ * _], in[_ * _], F, W;
int di[4] = { 0, 0, 1, -1 };
int dj[4] = { 1, -1, 0, 0 };
 
void ADD(int u, int v, int c, int w) { e[++cnte] = { v, H[u], c, w }, H[u] = cnte; }
void Add(int u, int v, int c, int w) { ADD(u, v, c, w), ADD(v, u, 0, -w); }
int id(int x, int y) { return (x - 1) * (m + 1) + y; }
std::pair<int,int> uid(int id) { int x = (id + m) / (m + 1), y = id - (x - 1) * (m + 1); return {x, y}; }
void Init() { std::queue <std::pair<int, int> > d;
    lep(i, 1, n) lep(j, 1, m) dis[i][j] = inf;
        d.push({1, 1}); pre[1][1] = -1, dis[1][1] = 1;
    while (!d.empty()) {
        int x = d.front().first, y = d.front().second; d.pop();
        if (x == 1 and y == m) continue;
        lep(k, 0, 3) {
            int nx = x + di[k], ny = y + dj[k];
            if (nx < 1 or ny < 1 or nx > n or ny > m or c[nx][ny] == 1) continue;
            if (pre[nx][ny]) { if (dis[nx][ny] == dis[x][y] + 1) pre[nx][ny] = std::min(pre[nx][ny], id(x, y)); }
            else dis[nx][ny] = dis[x][y] + 1, pre[nx][ny] = id(x, y), d.push({nx, ny});
        }
    }
     
    if (!pre[1][m]) { puts("Poor Y!"); exit(0); }
    int s = 1, t = m, nxt = pre[s][t]; nt[1][m] = nt[1][m + 1] = true;
    while (nxt and ~nxt) {
        auto d = uid(nxt);
        nxt = pre[s = d.first][t = d.second]; nt[s][t] = true;
    }
     
    lep(i, 1, n) {
        lep(j, 1, m) {
            if (c[i][j] == 1) {
                Add(id(i, j), id(i + 1, j), inf, 0);
                Add(id(i + 1, j), id(i, j), inf, 0);
                Add(id(i, j), id(i, j + 1), inf, 0);
                Add(id(i, j + 1), id(i, j), inf, 0);
                Add(id(i + 1, j), id(i + 1, j + 1), inf, 0);
                Add(id(i + 1, j + 1), id(i + 1, j), inf, 0);
                Add(id(i + 1, j + 1), id(i, j + 1), inf, 0);
                Add(id(i, j + 1), id(i + 1, j + 1), inf, 0);
            }
        }
    }
    lep(i, 1, k) {
        if (o[i] == 1 and (!nt[x[i] + 1][y[i]] or !nt[x[i]][y[i]]))
            Add(id(x[i] + 1, y[i]), id(x[i] + 1, y[i] + 1), 1, 1), Add(id(x[i] + 1, y[i] + 1), id(x[i] + 1, y[i]), 1, 1);
        if (o[i] == 2 and (!nt[x[i]][y[i] + 1] or !nt[x[i]][y[i]]))
            Add(id(x[i], y[i] + 1), id(x[i] + 1, y[i] + 1), 1, 1), Add(id(x[i] + 1, y[i] + 1), id(x[i], y[i] + 1), 1, 1);
    }
    S = 0, U = id(n + 1, m + 1) + 1, T = U + 1;
    Add(S, U, v, 0);
    lep(i, 2, n) Add(U, id(i, 1), inf, 0), Add(id(i, m + 1), T, inf, 0);
    lep(j, 2, m + 1) Add(id(n + 1, j), T, inf, 0);
}
bool Bfs() { std::queue <int> d;
    lep(i, S, T) dep[i] = inf, fro[i] = 0, vis[i] = false;
    d.push(S), in[S] = inf, dep[S] = 0; vis[S] = true;
    while (!d.empty()) {
        int u = d.front(); d.pop(); vis[u] = false;
        ep(i, u, v) if (e[i].c and dep[v] > dep[u] + e[i].w) {
            fro[v] = i, dep[v] = dep[u] + e[i].w;
            in[v] = std::min(e[i].c, in[u]);
            if (!vis[v]) vis[v] = true, d.push(v);
        }
    }
    return dep[T] != inf;
}
void EK() {
    while (Bfs()) {
        int nw = T, flow = in[T]; F += flow, W += flow * dep[T];
        while (nw != S) {
            e[fro[nw]].c -= flow, e[fro[nw] ^ 1].c += flow;
            nw = e[fro[nw] ^ 1].v;
        }
    }
}
 
int main() {
     
    scanf("%d%d%d%d", & n, & m, & v, & k);
    lep(i, 1, n) lep(j, 1, m) scanf("%d", c[i] + j);
    lep(i, 1, k) scanf("%d%d%d", x + i, y + i, o + i);
    Init(); EK();
    if (F != v) puts("Poor Y!");
    else printf("%d\n", W);
    return 0;
}

T2(element)

对于一个长度 \(> 2\)\(01\) 序列,我们对于原条件有充要条件:
将区间 \([l', r']\) 左右各加一个 \(1\), 变成 \([l, r]\)\(\exist\) \(l \le i < j < r\) 使得 :

  • \(a_i = a_{i+1} = 1\)
  • \(a_j = a_{j+1} = 1\)
  • \(i\equiv l\pmod 2\)
  • \(j \not\equiv l \pmod 2\)

\([l',r']\) 可行的充要条件。

充分性:

将满足条件的序列连续的 \(3\)\(0\) 缩成 \(1\) 个。
默认其他无限制的位置为全 \(0\) ,这不弱于我们当前的问题。
分别考虑 \(i=l\)\(j=r-1\) , \(i=j-1\) 的特殊情况,分别为
1011101011001101101011100110110
容易发现全部可行,所以充分。

必要性:

当长度为 \(3\) 时,我们可以枚举证明。
当长度 \(>3\) 时,可以归纳证明上述条件一定成立。
具体的,对于长度为 \(n\) 的可以合并出 \(1\) 的区间,其合并出 \(1\) 的第一步一定会得到一个 \(n - 2\) 的区间。
所以每个合法的长度为 \(n\) 的区间一定可以从一个长度为 \(n-2\) 的合法区间通过 0 \(\Rightarrow\) 000/001/010/1001 \(\Rightarrow\) 011/101/110/111 得到。
而在证明充分性时我们所列的 \(7\) 种情况,经过上述变换后同样满足上述形式。
例如 101 \(\Rightarrow\) 1 001 1

关于实现,我们对奇/偶数位各维护一个三元组 \(\left(l, r, w\right)\), 分别表示存在一个 \(i\) 的下界,存在一个 \(j\) 的下界,存在一个 \(\left(i, j \right)\) 点对的下界。
初始化的时候,\(i\) 与左侧取 \(min\)\(j\) 与右侧取 \(min\) ,因为原来的条件是补过 \(1\) 的。
因为最后需要补 \(1\) 来判断,所以查询时特殊处理。
详细的看代码。


#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
  
const int _ = 2e6 + 7;
typedef long long ll;
typedef unsigned int i32;
  
struct node { i32 l, r, w;
    node(i32 _l = 0, i32 _r = 0, i32 _w = 0) { l = _l, r = _r, w = _w; }
    friend node operator + (const node& x, const node& y) {
        return node(std::max(x.l, y.l), std::max(x.r, y.r),
            std::max(std::max(x.w, y.w), std::min(x.l, y.r)));
    }
}tr[_ << 2][2];
int n, m; i32 ans, seed, a[_];
  
unsigned _rnd(unsigned &_SEED) { _SEED ^= _SEED << 7; _SEED ^= _SEED >> 15; _SEED ^= _SEED << 9; return _SEED; }
void gen(const int &_N, unsigned &_SEED, int &l, int &r) { int len = _rnd(_SEED) % ((_N+1)/2) * 2; l = _rnd(_SEED) % (_N-len) + 1; r = l + len; }
#define ls p << 1
#define rs p << 1 | 1
void Build(int l, int r, int p) {
    if (l == r) return tr[p][l & 1] = node(std::min(a[l - 1], a[l]),
        std::min(a[l], a[l + 1]), std::min(a[l - 1], std::min(a[l], a[l + 1]))), void(); int mid = (l + r) >> 1;
    Build(l, mid, ls), Build(mid + 1, r, rs);
    tr[p][0] = tr[ls][0] + tr[rs][0], tr[p][1] = tr[ls][1] + tr[rs][1];
}
node Query(int l, int r, int s, int t, int type, int p) {
    if (l <= s and t <= r) return tr[p][type]; int mid = (s + t) >> 1;
    if (r <= mid) return Query(l, r, s, mid, type, ls);
    if (l > mid) return Query(l, r, mid + 1, t, type, rs);
    return Query(l, r, s, mid, type, ls) + Query(l, r, mid + 1, t, type, rs);
}
#undef ls
#undef rs
i32 Solve(int l, int r) {
    if (l == r) return a[l];
    node L = node(a[l], 0, std::min(a[l], a[l + 1])),
         R = node(0, a[r], std::min(a[r - 1], a[r]));
    return (L + Query(l + 1, r - 1, 1, n, l & 1, 1) + R).w;
}
  
int main() {
    scanf("%d%d", & n, & m);
    lep(i, 1, n) scanf("%u", a + i);
    Build(1, n, 1);
    scanf("%u", & seed); int l, r;
    lep(i, 1, m) gen(n, seed, l, r), ans += i * Solve(l, r);
      
    printf("%u\n", ans);
    return 0;
}
posted @ 2025-06-16 20:33  qkhm  阅读(16)  评论(1)    收藏  举报