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\) 的特殊情况,分别为
101
、110
、10110
、011
、01101
、01110
、0110110
。
容易发现全部可行,所以充分。
必要性:
当长度为 \(3\) 时,我们可以枚举证明。
当长度 \(>3\) 时,可以归纳证明上述条件一定成立。
具体的,对于长度为 \(n\) 的可以合并出 \(1\) 的区间,其合并出 \(1\) 的第一步一定会得到一个 \(n - 2\) 的区间。
所以每个合法的长度为 \(n\) 的区间一定可以从一个长度为 \(n-2\) 的合法区间通过0
\(\Rightarrow\)000/001/010/100
或1
\(\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;
}
时间仓促,如有错误欢迎指出,欢迎在评论区讨论,如对您有帮助还请点个推荐、关注支持一下