UOJ Round #11 简要题解

<style></style>

从这里开始

  说好的 agc 045 题解去哪了

Problem A 元旦老人与汉诺塔

  直接状压每个盘子在哪个柱子,记忆化搜索即可。

  时间复杂度 O(能过)。

Code

#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;

#define ull unsigned long long

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;
}

上一篇:LOJ 3161「NOI2019」I 君的探险「交互」「随机化」


下一篇:shell脚本案例-mysql备份脚本