UOJ Round #11 简要题解
从这里开始
说好的 agc 045 题解去哪了
Problem A 元旦老人与汉诺塔
直接状压每个盘子在哪个柱子,记忆化搜索即可。
时间复杂度 O(能过)。
Code
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;
#define ull __int128
const int Mod = 998244353;
int n, m;
typedef class Status {
public:
ull a, b, c;
int step;
Status() { }
Status(ull a, ull b, ull c, int step) : a(a), b(b), c(c), step(step) { }
void read() {
for (int i = 0, x; i < n; i++) {
scanf("%d", &x);
if (x == 1) {
a |= 1ull << i;
} else if (x == 2) {
b |= 1ull << i;
} else {
c |= 1ull << i;
}
}
}
bool operator < (Status t) const {
if (a ^ t.a) return a < t.a;
if (b ^ t.b) return b < t.b;
if (c ^ t.c) return c < t.c;
return step < t.step;
}
bool equal(Status t) {
return a == t.a && b == t.b && c == t.c;
}
} Status;
Status ss, st;
map<Status, int> f;
void inc(int& x) {
if (x >= Mod) {
x -= Mod;
}
}
int F(Status s) {
if (s.step > m) {
return 0;
}
if (f.count(s)) {
return f[s];
}
ull a = s.a & (-s.a);
ull b = s.b & (-s.b);
ull c = s.c & (-s.c);
int ret = s.equal(st);
if (a) {
if (a < b || !b) inc(ret += F(Status(s.a ^ a, s.b ^ a, s.c, s.step + 1)));
if (a < c || !c) inc(ret += F(Status(s.a ^ a, s.b, s.c ^ a, s.step + 1)));
}
if (b) {
if (b < a || !a) inc(ret += F(Status(s.a ^ b, s.b ^ b, s.c, s.step + 1)));
if (b < c || !c) inc(ret += F(Status(s.a, s.b ^ b, s.c ^ b, s.step + 1)));
}
if (c) {
if (c < a || !a) inc(ret += F(Status(s.a ^ c, s.b, s.c ^ c, s.step + 1)));
if (c < b || !b) inc(ret += F(Status(s.a, s.b ^ c, s.c ^ c, s.step + 1)));
}
return f[s] = ret;
}
int main() {
scanf("%d%d", &n, &m);
ss.read();
st.read();
int ans = F(ss);
printf("%d\n", ans);
return 0;
}
Problem B 元旦老人与丛林
注意到一个必要条件是任意一个点集 $V$ 的导出子图的边集大小不会超过 $2|V| - 2$。可以证明这个是充分的。
证明考虑归纳法,当 $n = 1$ 的时候显然满足。
现在考虑 $n > 1$ 的情况,并假设 $n$ 更小的时候成立。
考虑图中度数最小的 $x$,那么 $x$ 的度数只可能为 $0, 1, 2, 3$。
对于前 3 种情况可以简单地分配与它相邻的边使得它在 2 个新图中的度数均不超过 1。下面找考虑 $x$ 的度数为 3 的情况。
我们称一个子图 $G = (V, E)$ 是满的,当且仅当 $2|V| - 2 = |E|$。
引理 如果 $G_1 = (V_1, E_1), G_2 = (V_2, E_2)$ 都是满子图,并且 $V_1 \cap V_2 \neq nothing$,那么 $G = (V_1 \cup V_2, E_1 \cup E_2), G_3 = (V_1 \cap V_2, E_1 \cap E_2)$ 都是满子图。
证明 $|E_1 \cup E_2| = |E_1| + |E_2| - |E_1\cap E_2| \geqslant 2|V_1| - 2 + 2|V_2| - 2 - (2|V_1\cap V_2| - 2) = 2|V_1 \cup V_2| - 2$,又因为 $|E_1 \cup E_2| \leqslant 2|V_1 \cup V_2| - 2$,所以有 $|E_1 \cup E_2| =2|V_1 \cup V_2| - 2$。
用类似的方法可以证得 $G_3$ 是满子图。
考虑和 $x$ 相邻的三个点 $a, b, c$。设删掉 $x$ 及其相邻的边后得到的图是 $G'$。
定理 $(a, b), (b, c), (a, c)$ 中至少存在一对 $(u, v)$ 满足同时包含 $(u, v)$ 的导出子图都不是满子图
证明 考虑反证法,在这三对各找一个使得它是满子图的子图,然后把它们并起来,这样得到了一个同时包含 $a, b, c$ 的满子图,这时候加入 $x$ 和它相邻的边,显然此时不满足条件。
假设 $G'$ 中同时包含 $a, b$ 的导出子图都不是满子图,那么在 $G'$ 中加入边 $(a,b)$ 得到 $G''$,然后根据归纳假设有 $G''$ 是丛林。因此我们得到了两个森林,在包含 $(a, b)$ 的边的森林中,删除 $(a, b)$,然后加入 $(x, a), (x, b)$,再在另外一边加入 $(x, c)$。
剩下的问题是判断是否存在一个点集 $V$,它导出的边集 $E$,满足 $\frac{|E|}{|V| - 1} > 2$。
直接最大权闭合子图有点问题,因为它可能 $|V|$ 选空集,然后这样就会 GG。
每次硬点一个点必须选,退流就可以了。
时间复杂度 $O(nm)$。
Code
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;
const int inf = (signed) (~0u >> 2);
typedef class Edge {
public:
int ed, nx, r;
Edge() { }
Edge(int ed, int nx, int r) : ed(ed), nx(nx), r(r) { }
} Edge;
typedef class MapManager {
public:
vector<int> h;
vector<Edge> E;
MapManager() { }
MapManager(int n) {
h.assign(n + 1, -1);
}
void add_edge(int u, int v, int c) {
E.emplace_back(v, h[u], c);
h[u] = (signed) E.size() - 1;
}
Edge& operator [] (int p) {
return E[p];
}
} MapManager;
typedef class Network {
public:
int S, T;
MapManager g;
vector<int> div;
int rest_flow;
Network() { }
Network(int S, int T) : S(S), T(T), g(T + 1) {
div.resize(T + 1);
}
bool bfs() {
queue<int> Q;
fill(div.begin(), div.end(), -1);
Q.push(S);
div[S] = 0;
while (!Q.empty()) {
int p = Q.front();
Q.pop();
if (p == T) {
continue;
}
for (int i = g.h[p]; ~i; i = g[i].nx) {
int e = g[i].ed;
if (!g[i].r || ~div[e]) {
continue;
}
div[e] = div[p] + 1;
Q.push(e);
}
}
return div[T] != -1;
}
vector<int> cur;
int dfs(int p, int mf) {
if (p == T || !mf) {
rest_flow -= mf;
if (!rest_flow) {
throw 1;
}
return mf;
}
int flow = 0, f;
for (int& i = cur[p]; ~i; i = g[i].nx) {
int e = g[i].ed;
if (div[e] == div[p] + 1 && (f = dfs(e, min(mf, g[i].r))) > 0) {
flow += f;
g[i].r -= f;
g[i ^ 1].r += f;
mf -= f;
if (!mf) {
break;
}
}
}
return flow;
}
int dinic() {
int ret = 0;
try {
while (bfs()) {
cur = g.h;
ret += dfs(S, inf);
}
} catch (int) {
return 2;
}
return ret;
}
void add_edge(int u, int v, int c) {
g.add_edge(u, v, c);
g.add_edge(v, u, 0);
}
vector<bool> get_S() {
vector<bool> vis (T + 1, false);
queue<int> Q;
Q.push(S);
vis[S] = true;
while (!Q.empty()) {
int p = Q.front();
Q.pop();
for (int i = g.h[p]; ~i; i = g[i].nx) {
int e = g[i].ed;
if (!vis[e] && g[i].r) {
vis[e] = true;
Q.push(e);
}
}
}
return vis;
}
} Network;
int n, m, lim;
int main() {
scanf("%d%d", &n, &m);
lim = 2;
int T;
Network network(0, T = n + m + 1);
vector<int> deg (n + 1, 0);
vector<pair<int, int>> E (m);
for (int i = 0, u, v; i < m; i++) {
scanf("%d%d", &u, &v);
if (u == v) {
puts("No");
return 0;
}
E[i] = make_pair(u, v);
++deg[u];
++deg[v];
}
for (int i = 1; i <= n; i++) {
if (deg[i] >= lim) {
network.add_edge(i, T, lim);
}
}
int cost = 0;
for (int i = 1, u, v; i <= m; i++) {
u = E[i - 1].first, v = E[i - 1].second;
if (deg[u] >= lim && deg[v] >= lim) {
network.add_edge(0, n + i, 1);
network.add_edge(n + i, u, inf);
network.add_edge(n + i, v, inf);
++cost;
}
}
network.rest_flow = inf;
cost -= network.dinic();
if (cost) {
puts("No");
return 0;
} else {
auto inS = network.get_S();
for (int i = 1; i <= n; i++) {
if (inS[i] && deg[i] >= lim) {
puts("No");
return 0;
}
}
for (int i = 1; i <= n; i++) {
if (deg[i] < lim) {
continue;
}
Network network1 = network;
network1.add_edge(0, i, lim);
network1.rest_flow = 2;
int xx = network1.dinic();
if (xx < lim) {
puts("No");
return 0;
}
}
}
puts("Yes");
return 0;
}
Problem C 元旦老人与数列
若干 segment tree beats 板题之一。
考虑 segment tree beats 本质上是对区间的最小值打标记。当最小值集合发生改变的时候暴力递归。
所以对最小值和非区间最小值分别维护区间加标记。
因为要查询区间历史最小值,所以再维护一下历史最小值,以及最小值和非最小值的加标记的最小前缀和。
Code
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;
typedef class Input {
protected:
const static int limit = 65536;
FILE* file;
int ss, st;
char buf[limit];
public:
Input() : file(NULL) { };
Input(FILE* file) : file(file) { }
void open(FILE *file) {
this->file = file;
}
void open(const char* filename) {
file = fopen(filename, "r");
}
char pick() {
if (ss == st)
st = fread(buf, 1, limit, file), ss = 0;//, cerr << "str: " << buf << "ed " << st << endl;
return buf[ss++];
}
} Input;
#define digit(_x) ((_x) >= '0' && (_x) <= '9')
Input& operator >> (Input& in, unsigned& u) {
char x;
while (~(x = in.pick()) && !digit(x));
for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
return in;
}
Input& operator >> (Input& in, unsigned long long& u) {
char x;
while (~(x = in.pick()) && !digit(x));
for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
return in;
}
Input& operator >> (Input& in, int& u) {
char x;
while (~(x = in.pick()) && !digit(x) && x != '-');
int aflag = ((x == '-') ? (x = in.pick(), -1) : (1));
for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
u *= aflag;
return in;
}
Input& operator >> (Input& in, long long& u) {
char x;
while (~(x = in.pick()) && !digit(x) && x != '-');
int aflag = ((x == '-') ? (x = in.pick(), -1) : (1));
for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
u *= aflag;
return in;
}
Input& operator >> (Input& in, double& u) {
char x;
while (~(x = in.pick()) && !digit(x) && x != '-');
int aflag = ((x == '-') ? (x = in.pick(), -1) : (1));
for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
if (x == '.') {
double dec = 1;
for ( ; ~(x = in.pick()) && digit(x); u = u + (dec *= 0.1) * (x - '0'));
}
u *= aflag;
return in;
}
Input& operator >> (Input& in, char* str) {
char x;
while (~(x = in.pick()) && x != '\n' && x != ' ')
*(str++) = x;
*str = 0;
return in;
}
Input in (stdin);
typedef class Output {
protected:
const static int Limit = 65536;
char *tp, *ed;
char buf[Limit];
FILE* file;
int precision;
void flush() {
fwrite(buf, 1, tp - buf, file);
fflush(file);
tp = buf;
}
public:
Output() { }
Output(FILE* file) : tp(buf), ed(buf + Limit), file(file), precision(6) { }
Output(const char *str) : tp(buf), ed(buf + Limit), precision(6) {
file = fopen(str, "w");
}
~Output() {
flush();
}
void put(char x) {
if (tp == ed)
flush();
*(tp++) = x;
}
int get_precision() {
return precision;
}
void set_percision(int x) {
precision = x;
}
} Output;
Output& operator << (Output& out, int x) {
static char buf[35];
static char * const lim = buf + 34;
if (!x)
out.put('0');
else {
if (x < 0)
out.put('-'), x = -x;
char *tp = lim;
for ( ; x; *(--tp) = x % 10, x /= 10);
for ( ; tp != lim; out.put(*(tp++) + '0'));
}
return out;
}
Output& operator << (Output& out, long long x) {
static char buf[36];
static char * const lim = buf + 34;
if (!x)
out.put('0');
else {
if (x < 0)
out.put('-'), x = -x;
char *tp = lim;
for ( ; x; *(--tp) = x % 10, x /= 10);
for ( ; tp != lim; out.put(*(tp++) + '0'));
}
return out;
}
Output& operator << (Output& out, unsigned x) {
static char buf[35];
static char * const lim = buf + 34;
if (!x)
out.put('0');
else {
char *tp = lim;
for ( ; x; *(--tp) = x % 10, x /= 10);
for ( ; tp != lim; out.put(*(tp++) + '0'));
}
return out;
}
Output& operator << (Output& out, char x) {
out.put(x);
return out;
}
Output& operator << (Output& out, const char* str) {
for ( ; *str; out.put(*(str++)));
return out;
}
Output& operator << (Output& out, double x) {
int y = x;
x -= y;
out << y << '.';
for (int i = out.get_precision(); i; i--, y = x * 10, x = x * 10 - y, out.put(y + '0'));
return out;
}
Output out (stdout);
const int N = 5e5 + 5;
const int inf = (~0u >> 1);
#define ll long long
typedef class SegTreeNode {
public:
int mi, mi2;
int tga, tga2;
int hmi, mtga, mtga2;
SegTreeNode *l, *r;
void init(int x) {
mi = x;
mi2 = inf;
hmi = x;
tga = tga2 = mtga = mtga2 = 0;
l = r = NULL;
}
void upd(int _tga, int _tga2, int _mtga, int _mtga2) {
hmi = min(hmi, mi + _mtga);
mi += _tga;
(mi2 != inf) && (mi2 += _tga2, 0);
mtga = min(mtga, tga + _mtga);
mtga2 = min(mtga2, tga2 + _mtga2);
tga += _tga;
tga2 += _tga2;
}
void upd(int v) {
if (v >= mi2) {
push_down();
l->upd(v);
r->upd(v);
push_up();
} else if (v > mi) {
tga += v - mi;
mtga = min(mtga, tga);
mi = v;
}
}
void push_up() {
hmi = min(l->hmi, r->hmi);
mi = min(l->mi, r->mi);
if (l->mi == r->mi) {
mi2 = min(l->mi2, r->mi2);
} else if (l->mi < r->mi) {
mi2 = min(l->mi2, r->mi);
} else {
mi2 = min(l->mi, r->mi2);
}
}
void push_down() {
if (tga || tga2 || mtga || mtga2) {
if (l->mi + tga == mi) {
l->upd(tga, tga2, mtga, mtga2);
} else {
l->upd(tga2, tga2, mtga2, mtga2);
}
if (r->mi + tga == mi) {
r->upd(tga, tga2, mtga, mtga2);
} else {
r->upd(tga2, tga2, mtga2, mtga2);
}
tga = tga2 = mtga = mtga2 = 0;
}
}
} SegTreeNode;
SegTreeNode pool[N << 1];
SegTreeNode* _top = pool;
typedef class SegmentTree {
public:
int n;
SegTreeNode* rt;
void build(SegTreeNode*& p, int l, int r, int* a) {
p = _top++;
if (l == r) {
p->init(a[l]);
} else {
int mid = (l + r) >> 1;
build(p->l, l, mid, a);
build(p->r, mid + 1, r, a);
p->push_up();
}
}
void build(int n, int* a) {
this->n = n;
build(rt, 1, n, a);
}
void update_add(SegTreeNode* p, int l, int r, int ql, int qr, int a) {
if (l == ql && r == qr) {
p->upd(a, a, a, a);
return;
}
p->push_down();
int mid = (l + r) >> 1;
if (qr <= mid) {
update_add(p->l, l, mid, ql, qr, a);
} else if (ql > mid) {
update_add(p->r, mid + 1, r, ql, qr, a);
} else {
update_add(p->l, l, mid, ql, mid, a);
update_add(p->r, mid + 1, r, mid + 1, qr, a);
}
p->push_up();
}
void update_add(int l, int r, int a) {
update_add(rt, 1, n, l, r, a);
}
void update_mx(SegTreeNode* p, int l, int r, int ql, int qr, int v) {
if (l == ql && r == qr) {
p->upd(v);
return;
}
p->push_down();
int mid = (l + r) >> 1;
if (qr <= mid) {
update_mx(p->l, l, mid, ql, qr, v);
} else if (ql > mid) {
update_mx(p->r, mid + 1, r, ql, qr, v);
} else {
update_mx(p->l, l, mid, ql, mid, v);
update_mx(p->r, mid + 1, r, mid + 1, qr, v);
}
p->push_up();
}
void update_mx(int l, int r, int v) {
update_mx(rt, 1, n, l, r, v);
}
int query_mi(SegTreeNode* p, int l, int r, int ql, int qr) {
if (l == ql && r == qr) {
return p->mi;
}
p->push_down();
int mid = (l + r) >> 1;
if (qr <= mid) {
return query_mi(p->l, l, mid, ql, qr);
} else if (ql > mid) {
return query_mi(p->r, mid + 1, r, ql, qr);
}
int a = query_mi(p->l, l, mid, ql, mid);
int b = query_mi(p->r, mid + 1, r, mid + 1, qr);
return min(a, b);
}
int query_mi(int l, int r) {
return query_mi(rt, 1, n, l, r);
}
int query_hmi(SegTreeNode* p, int l, int r, int ql, int qr) {
if (l == ql && r == qr) {
return p->hmi;
}
p->push_down();
int mid = (l + r) >> 1;
if (qr <= mid) {
return query_hmi(p->l, l, mid, ql, qr);
} else if (ql > mid) {
return query_hmi(p->r, mid + 1, r, ql, qr);
}
int a = query_hmi(p->l, l, mid, ql, mid);
int b = query_hmi(p->r, mid + 1, r, mid + 1, qr);
return min(a, b);
}
int query_hmi(int l, int r) {
return query_hmi(rt, 1, n, l, r);
}
} SegmentTree;
int n, m;
int a[N];
SegmentTree st;
int main() {
in >> n >> m;
for (int i = 1; i <= n; i++) {
in >> a[i];
}
st.build(n, a);
int op, l, r, x;
while (m--) {
in >> op >> l >> r;
if (op <= 2) {
in >> x;
if (op == 1) {
st.update_add(l, r, x);
} else{
st.update_mx(l, r, x);
}
} else if (op == 3) {
out << st.query_mi(l, r) << '\n';
} else {
out << st.query_hmi(l, r) << '\n';
}
}
return 0;
}
浙公网安备 33010602011771号