UOJ Round #11
A
记忆化一下,直接爆搜即可。
状态数非常少,分析出的上界大约是 \(10^7\) 左右,但实际上只有 \(10^5\) 的样子。
#include <bits/stdc++.h>
#define IL __inline__ __attribute__((always_inline))
#define For(i, a, b) for (int i = (a), i##end = (b); i <= i##end; ++ i)
#define FOR(i, a, b) for (int i = (a), i##end = (b); i < i##end; ++ i)
#define Rep(i, a, b) for (int i = (a), i##end = (b); i >= i##end; -- i)
#define REP(i, a, b) for (int i = (a) - 1, i##end = (b); i >= i##end; -- i)
typedef long long LL;
template <class T>
IL bool chkmax(T &a, const T &b) {
return a < b ? ((a = b), 1) : 0;
}
template <class T>
IL bool chkmin(T &a, const T &b) {
return a > b ? ((a = b), 1) : 0;
}
template <class T>
IL T mymax(const T &a, const T &b) {
return a > b ? a : b;
}
template <class T>
IL T mymin(const T &a, const T &b) {
return a < b ? a : b;
}
template <class T>
IL T myabs(const T &a) {
return a > 0 ? a : -a;
}
const int INF = 0X3F3F3F3F;
const double EPS = 1E-8, PI = acos(-1.0);
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
#define OK DEBUG("Passing [%s] in LINE %d...\n", __FUNCTION__, __LINE__)
#define SZ(x) ((int)(x).size())
namespace Math {
const int MOD = 998244353;
IL int add(int a, int b) {
a += b;
return a >= MOD ? a - MOD : a;
}
template <class ...Args>
IL int add(int a, const Args &...args) {
a += add(args...);
return a >= MOD ? a - MOD : a;
}
IL int sub(int a, int b) {
a -= b;
return a < 0 ? a + MOD : a;
}
IL int mul(int a, int b) {
return (LL)a * b % MOD;
}
template <class ...Args>
IL int mul(int a, const Args &...args) {
return (LL)a * mul(args...) % MOD;
}
IL int quickPow(int a, int p) {
int ret = 1;
for (; p; p >>= 1, a = mul(a, a)) {
if (p & 1) {
ret = mul(ret, a);
}
}
return ret;
}
}
using namespace Math;
const int MAXN = 100 + 5;
using Arr = std::array<int, 100>;
using Node = std::pair<int, Arr>;
Arr s, t;
int n, m;
std::map<Node, int> mp;
int DFS(int c, const Arr &cur) {
if (c >= m) {
return cur == t;
}
Node now = Node(c, cur);
auto it = mp.find(now);
if (it != mp.end()) {
return it->second;
}
Arr t = cur;
int x = t[0], ans = cur == ::t;
FOR(i, 0, 3) {
if (x != i) {
t[0] = i;
ans = add(ans, DFS(c + 1, t));
t[0] = x;
}
}
FOR(i, 1, n) {
int y = t[i];
if (y != x) {
FOR(j, 0, 3) {
if (x != j && y != j) {
t[i] = j;
ans = add(ans, DFS(c + 1, t));
t[i] = y;
}
}
break;
}
}
return mp[now] = ans;
}
int main() {
scanf("%d%d", &n, &m);
FOR(i, 0, n) {
int x;
scanf("%d", &x);
-- x;
s[i] = x;
}
FOR(i, 0, n) {
int x;
scanf("%d", &x);
-- x;
t[i] = x;
}
printf("%d\n", DFS(0, s));
return 0;
}
B
有一个结论是如果任意点集 \(V\) 都满足 \(E(V) \le 2|V| - 2\),那么这个图就是丛林,反之则不是,可以用拟阵并证明。
那么就很好做了,建一个二分图,边向点连边,边权值是 \(1\),点权值是 \(-2\),跑最大权闭合子图即可。
这样有一个问题是最大权闭合子图权值可能是负的,这个时候网络流会跑出一个空集来,于是你需要强制选一个点(把它的权值改成 \(0\))。
每个点都跑一遍 Dinic 显然不优,换成退流即可。
#include <bits/stdc++.h>
#define IL __inline__ __attribute__((always_inline))
#define For(i, a, b) for (int i = (a), i##end = (b); i <= i##end; ++ i)
#define FOR(i, a, b) for (int i = (a), i##end = (b); i < i##end; ++ i)
#define Rep(i, a, b) for (int i = (a), i##end = (b); i >= i##end; -- i)
#define REP(i, a, b) for (int i = (a) - 1, i##end = (b); i >= i##end; -- i)
typedef long long LL;
template <class T>
IL bool chkmax(T &a, const T &b) {
return a < b ? ((a = b), 1) : 0;
}
template <class T>
IL bool chkmin(T &a, const T &b) {
return a > b ? ((a = b), 1) : 0;
}
template <class T>
IL T mymax(const T &a, const T &b) {
return a > b ? a : b;
}
template <class T>
IL T mymin(const T &a, const T &b) {
return a < b ? a : b;
}
template <class T>
IL T myabs(const T &a) {
return a > 0 ? a : -a;
}
const int INF = 0X3F3F3F3F;
const double EPS = 1E-8, PI = acos(-1.0);
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
#define OK DEBUG("Passing [%s] in LINE %d...\n", __FUNCTION__, __LINE__)
#define SZ(x) ((int)(x).size())
const int MAXN = 6000 + 5, MAXM = 100000 + 5;
namespace NetworkFlow {
int s, t, n, m;
struct Edge {
int from, to, resi;
Edge() {}
Edge(int _f, int _t, int _r) : from(_f), to(_t), resi(_r) {}
} edge[MAXM * 2];
std::vector<int> adj[MAXN];
int cnt;
IL void addEdge(int u, int v, int c) {
edge[cnt] = Edge(u, v, c), adj[u].push_back(cnt);
++ cnt;
edge[cnt] = Edge(v, u, 0), adj[v].push_back(cnt);
++ cnt;
}
IL void build() {
scanf("%d%d", &n, &m);
s = n + m + 1, t = n + m + 2;
For(i, 1, n) {
addEdge(i, t, 2);
}
For(i, 1, m) {
addEdge(s, i + n, 1);
}
For(i, 1, m) {
int u, v;
scanf("%d%d", &u, &v);
addEdge(i + n, u, INF);
addEdge(i + n, v, INF);
}
}
int dist[MAXN], vis[MAXN], rc;
IL bool BFS() {
static int queue[MAXN];
dist[s] = 0;
vis[s] = ++ rc;
int l = 1, r = 0;
queue[++ r] = s;
while (l <= r) {
int u = queue[l ++];
for (auto &x : adj[u]) {
Edge &e = edge[x];
if (e.resi && vis[e.to] != rc) {
dist[e.to] = dist[u] + 1;
vis[e.to] = rc;
queue[++ r] = e.to;
}
}
}
return vis[t] == rc;
}
int cur[MAXN];
int DFS(int u, int x) {
if (!x || u == t) {
return x;
}
int flow = 0, f;
for (int &i = cur[u]; i < SZ(adj[u]); ++ i) {
Edge &e = edge[adj[u][i]];
if (dist[e.to] == dist[u] + 1 && (f = DFS(e.to, mymin(e.resi, x)))) {
edge[adj[u][i]].resi -= f, edge[adj[u][i] ^ 1].resi += f;
flow += f, x -= f;
if (!x) {
break;
}
}
}
return flow;
}
IL int dinic(int p = s, int q = t, int init = INF) {
if (p == q) {
return init;
}
int flow = 0, a = s, b = t;
s = p, t = q;
while (init > flow && BFS()) {
memset(cur, 0, sizeof cur);
flow += DFS(s, init - flow);
}
s = a, t = b;
return flow;
}
IL bool solve() {
int w = dinic();
if (w < m) {
return 0;
}
For(i, 1, n) {
int a = (i - 1) << 1, b = (i - 1) << 1 | 1, u = edge[a].from, v = edge[a].to, p = edge[b].resi;
dinic(t, v, p), dinic(u, s, p);
w -= p;
edge[a].resi = edge[b].resi = 0;
w += dinic(s, t, 2);
if (w < m) {
return 0;
}
edge[a].resi = 2;
}
return 1;
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("forest.in", "r", stdin);
#endif
NetworkFlow::build();
puts(NetworkFlow::solve() ? "Yes" : "No");
return 0;
}
C
参照吉如一的集训队论文,用 Segment Tree Beats 的方法维护操作 2,其他操作把区间的最小值和非最小值部分分开维护就行了。
#include <bits/stdc++.h>
#define IL __inline__ __attribute__((always_inline))
#define For(i, a, b) for (int i = (a), i##end = (b); i <= i##end; ++ i)
#define FOR(i, a, b) for (int i = (a), i##end = (b); i < i##end; ++ i)
#define Rep(i, a, b) for (int i = (a), i##end = (b); i >= i##end; -- i)
#define REP(i, a, b) for (int i = (a) - 1, i##end = (b); i >= i##end; -- i)
typedef long long LL;
template <class T>
IL bool chkmax(T &a, const T &b) {
return a < b ? ((a = b), 1) : 0;
}
template <class T>
IL bool chkmin(T &a, const T &b) {
return a > b ? ((a = b), 1) : 0;
}
template <class T>
IL T mymax(const T &a, const T &b) {
return a > b ? a : b;
}
template <class T>
IL T mymin(const T &a, const T &b) {
return a < b ? a : b;
}
template <class T>
IL T myabs(const T &a) {
return a > 0 ? a : -a;
}
const int INF = 0X3F3F3F3F;
const double EPS = 1E-8, PI = acos(-1.0);
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
#define OK DEBUG("Passing [%s] in LINE %d...\n", __FUNCTION__, __LINE__)
#define SZ(x) ((int)(x).size())
struct Input {
char buf[1 << 25], *s;
Input() {
#ifndef ONLINE_JUDGE
freopen("array.in", "r", stdin);
freopen("array.out", "w", stdout);
#endif
fread(s = buf, 1, 1 << 25, stdin);
}
friend Input &operator>>(Input &io, int &x) {
x = 0;
int p = 1;
while (!isdigit(*io.s)) {
if (*io.s == '-') {
p = -1;
}
++ io.s;
}
while (isdigit(*io.s)) {
x = x * 10 + *io.s ++ - '0';
}
x *= p;
return io;
}
} cin;
const int MAXN = 500000 + 5;
struct SegmentTree {
using Arr = std::array<int, MAXN * 4>;
Arr min, min_num, min_mark, sec, mark, min_hmin, min_pmark, hmin, pmark;
#define lc (o << 1)
#define rc (o << 1 | 1)
void pushUp(int o, int l, int r) {
int mid = (l + r) >> 1;
min_mark[o] = mark[o] = min_pmark[o] = pmark[o] = 0;
if (min[lc] < min[rc]) {
min[o] = min[lc], min_num[o] = min_num[lc];
min_hmin[o] = min_hmin[lc], hmin[o] = min_hmin[rc];
if (min_num[lc] != mid - l + 1) {
chkmin(hmin[o], hmin[lc]);
}
if (min_num[rc] != r - mid) {
chkmin(hmin[o], hmin[rc]);
}
sec[o] = min_num[lc] == mid - l + 1 ? min[rc] : mymin(sec[lc], min[rc]);
} else if (min[lc] > min[rc]) {
min[o] = min[rc], min_num[o] = min_num[rc];
min_hmin[o] = min_hmin[rc], hmin[o] = min_hmin[lc];
if (min_num[lc] != mid - l + 1) {
chkmin(hmin[o], hmin[lc]);
}
if (min_num[rc] != r - mid) {
chkmin(hmin[o], hmin[rc]);
}
sec[o] = min_num[rc] == r - mid ? min[lc] : mymin(sec[rc], min[lc]);
} else {
min[o] = min[lc], min_num[o] = min_num[lc] + min_num[rc];
min_hmin[o] = mymin(min_hmin[lc], min_hmin[rc]);
if (min_num[lc] != mid - l + 1 && min_num[rc] != r - mid) {
hmin[o] = mymin(hmin[lc], hmin[rc]);
} else if (min_num[lc] != mid - l + 1) {
hmin[o] = hmin[lc];
} else if (min_num[rc] != r - mid) {
hmin[o] = hmin[rc];
} else {
hmin[o] = 0;
}
if (min_num[lc] == mid - l + 1) {
sec[o] = sec[rc];
} else if (min_num[rc] == r - mid) {
sec[o] = sec[lc];
} else {
sec[o] = mymin(sec[lc], sec[rc]);
}
}
}
void pushDown(int o, int l, int r) {
int mid = (l + r) >> 1;
if (min_pmark[o]) {
if (min[lc] < min[rc]) {
chkmin(min_hmin[lc], min[lc] + min_pmark[o]);
chkmin(min_pmark[lc], min_mark[lc] + min_pmark[o]);
} else if (min[lc] < min[rc]) {
chkmin(min_hmin[rc], min[rc] + min_pmark[o]);
chkmin(min_pmark[rc], min_mark[rc] + min_pmark[o]);
} else {
chkmin(min_hmin[lc], min[lc] + min_pmark[o]), chkmin(min_hmin[rc], min[rc] + min_pmark[o]);
chkmin(min_pmark[lc], min_mark[lc] + min_pmark[o]), chkmin(min_pmark[rc], min_mark[rc] + min_pmark[o]);
}
min_pmark[o] = 0;
}
if (pmark[o]) {
if (min[lc] < min[rc]) {
chkmin(min_hmin[rc], min[rc] + pmark[o]);
chkmin(min_pmark[rc], min_mark[rc] + pmark[o]);
} else if (min[lc] > min[rc]) {
chkmin(min_hmin[lc], min[lc] + pmark[o]);
chkmin(min_pmark[lc], min_mark[lc] + pmark[o]);
}
if (min_num[lc] != mid - l + 1) {
chkmin(hmin[lc], sec[lc] + pmark[o]);
chkmin(pmark[lc], mark[lc] + pmark[o]);
}
if (min_num[rc] != r - mid) {
chkmin(hmin[rc], sec[rc] + pmark[o]);
chkmin(pmark[rc], mark[rc] + pmark[o]);
}
pmark[o] = 0;
}
int x = min[lc], y = min[rc];
if (min_mark[o]) {
if (x < y) {
min[lc] += min_mark[o], min_mark[lc] += min_mark[o];
} else if (x > y) {
min[rc] += min_mark[o], min_mark[rc] += min_mark[o];
} else {
min[lc] += min_mark[o], min[rc] += min_mark[o], min_mark[lc] += min_mark[o], min_mark[rc] += min_mark[o];
}
min_mark[o] = 0;
}
if (mark[o]) {
if (x < y) {
min[rc] += mark[o], min_mark[rc] += mark[o];
} else if (x > y) {
min[lc] += mark[o], min_mark[lc] += mark[o];
}
if (min_num[lc] != mid - l + 1) {
sec[lc] += mark[o], mark[lc] += mark[o];
}
if (min_num[rc] != r - mid) {
sec[rc] += mark[o], mark[rc] += mark[o];
}
mark[o] = 0;
}
}
void build(int o, int l, int r, int *a) {
if (l == r) {
min[o] = min_hmin[o] = a[l];
min_num[o] = 1;
return;
}
int mid = (l + r) >> 1;
build(lc, l, mid, a);
build(rc, mid + 1, r, a);
pushUp(o, l, r);
}
void modify1(int o, int l, int r, int a, int b, int x) {
if (l >= a && r <= b) {
min[o] += x, min_mark[o] += x;
chkmin(min_hmin[o], min[o]), chkmin(min_pmark[o], min_mark[o]);
if (min_num[o] != r - l + 1) {
sec[o] += x, mark[o] += x;
chkmin(hmin[o], sec[o]), chkmin(pmark[o], mark[o]);
}
return;
}
pushDown(o, l, r);
int mid = (l + r) >> 1;
if (a <= mid) {
modify1(lc, l, mid, a, b, x);
}
if (b > mid) {
modify1(rc, mid + 1, r, a, b, x);
}
pushUp(o, l, r);
}
void modify2(int o, int l, int r, int a, int b, int x) {
if (l >= a && r <= b) {
if (x <= min[o]) {
return;
} else if (min_num[o] == r - l + 1 || x < sec[o]) {
int d = x - min[o];
min[o] += d, min_mark[o] += d;
chkmin(min_hmin[o], min[o]), chkmin(min_pmark[o], min_mark[o]);
return;
}
}
pushDown(o, l, r);
int mid = (l + r) >> 1;
if (a <= mid) {
modify2(lc, l, mid, a, b, x);
}
if (b > mid) {
modify2(rc, mid + 1, r, a, b, x);
}
pushUp(o, l, r);
}
int query1(int o, int l, int r, int a, int b) {
if (l >= a && r <= b) {
return min[o];
}
pushDown(o, l, r);
int mid = (l + r) >> 1, ans = INT_MAX;
if (a <= mid) {
chkmin(ans, query1(lc, l, mid, a, b));
}
if (b > mid) {
chkmin(ans, query1(rc, mid + 1, r, a, b));
}
return ans;
}
int query2(int o, int l, int r, int a, int b) {
if (l >= a && r <= b) {
return min_num[o] == r - l + 1 ? min_hmin[o] : mymin(min_hmin[o], hmin[o]);
}
pushDown(o, l, r);
int mid = (l + r) >> 1, ans = INT_MAX;
if (a <= mid) {
chkmin(ans, query2(lc, l, mid, a, b));
}
if (b > mid) {
chkmin(ans, query2(rc, mid + 1, r, a, b));
}
return ans;
}
} seg;
int a[MAXN];
int main() {
int n, m;
cin >> n >> m;
For(i, 1, n) {
cin >> a[i];
}
seg.build(1, 1, n, a);
For(i, 1, m) {
int opt;
cin >> opt;
if (opt == 1) {
int l, r, c;
cin >> l >> r >> c;
seg.modify1(1, 1, n, l, r, c);
} else if (opt == 2) {
int l, r, d;
cin >> l >> r >> d;
seg.modify2(1, 1, n, l, r, d);
} else if (opt == 3) {
int l, r;
cin >> l >> r;
printf("%d\n", seg.query1(1, 1, n, l, r));
} else {
int l, r;
cin >> l >> r;
printf("%d\n", seg.query2(1, 1, n, l, r));
}
}
return 0;
}