Educational Codeforces Round 97 简要题解

Educational Codeforces Round 97 简要题解

缺省源

#include <queue>
#include <vector>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MP make_pair
#define ll long long
#define fi first
#define se second
using namespace std;

template <typename T>
void read(T &x) {
    x = 0; bool f = 0;
    char c = getchar();
    for (;!isdigit(c);c=getchar()) if (c=='-') f=1;
    for (;isdigit(c);c=getchar()) x=x*10+(c^48);
    if (f) x=-x;
}

template<typename F>
inline void write(F x, char ed = '
') {
	static short st[30];short tp=0;
	if(x<0) putchar('-'),x=-x;
	do st[++tp]=x%10,x/=10; while(x);
	while(tp) putchar('0'|st[tp--]);
	putchar(ed);
}

template <typename T>
inline void Mx(T &x, T y) { x < y && (x = y); }

template <typename T>
inline void Mn(T &x, T y) { x > y && (x = y); }

A. Marketing Scheme

直接判断 (l imes 2 ge r) 即可。

B. Reverse Binary Strings

这题让好多人想了一会(我也,考虑对序列异或差分,最终序列要求全部事 1,每次 reverse 可以发现只有两端的值变,所以每次可以同时消掉两个 0

C. Chef Monocarp

简单 dp 即可

D. Minimal Height Tree

模拟 bfs 即可,找到最长的连续段当作一串儿子挂在一个节点上。

const int N = 200050;
int dep[N], a[N], T, n;
int main() {
	for (read(T); T; T--) {
		read(n);
		memset(dep, 0, n * 4 + 200);
		queue<int> q; q.push(1);
		for (int i = 1;i <= n; i++) read(a[i]);
		for (int i = 2;i <= n; i++) {
			int t = q.front();
			dep[i] = dep[t] + 1, q.push(i);
			if (a[i] >= a[i+1]) q.pop();
		}
		write(dep[n]);	
	}
	return 0;
}

E. Make It Increasing

先考虑序列的合法情况,显然严格上升序列可以通过 (a[i] = a[i] - i) 变成非严格上升序列,所以我们先正着反着扫两遍,得出是否有解和哪些位置必须要改变,剩下的位置做最长上升子序列即可。

const int N = 500050;
int st[N], a[N], vis[N], n, k, tp;
void insert(int x) {
	if (!tp) return st[++tp] = x, void();
	int l = 1, r = tp;
	while (l <= r) {
		int mid = (l + r) >> 1;
		if (st[mid] <= x) l = mid + 1;
		else r = mid - 1;
	}
	if (l > tp) st[++tp] = x;
	else st[l] = x;
}

int main() {
	read(n), read(k);
	for (int i = 1;i <= n; i++) read(a[i]), a[i] -= i;
	for (int i = 1, x;i <= k; i++) read(x), vis[x] = 1;
	int lim = -1e9;
	for (int i = 1;i <= n; i++) {
		if (vis[i] == 1) {
			if (a[i] < lim) return write(-1), 0;
			Mx(lim, a[i]);
		}
		if (a[i] < lim) vis[i] = -1;
	}
	lim = 1e9;
	for (int i = n;i >= 1; i--) {
		if (vis[i] == 1) {
			if (a[i] > lim) return write(-1), 0;
			Mn(lim, a[i]);
		}
		if (a[i] > lim) vis[i] = -1;
	}
	memset(st, 0x3f, sizeof(st)); 
	st[tp = 0] = 0;
	for (int i = 1;i <= n; i++) {
		if (vis[i]) continue;
		insert(a[i]);
	}
	write(n - k - tp);
	return 0;
}

F. Emotional Fishermen

考后自己补出来的。

假设我们已经获得了最终的序列,把前缀最大值看成隔板,首先两个前缀最大值 (a_i, a_{i+1}) 要满足 (2a_ile a_{i+1}),考虑其他元素 x,发现在第一个大于等于 (2x) 前缀最大值之后就可以随便填了。因此我们一个一个放前缀最大值,每放一个最大值,发现有一些元素可以随便放了,用排列算一下即可。

dp 的过程发现还可以前缀和优化,吊打标算了

const int N = 5005;
const int P = 998244353;
ll fac[N], inv[N], pre[N], dp[N], a[N], n;
ll A(int n, int m) { return fac[n] * inv[n-m] % P; }
int cnt[N];
ll fpw(ll x, ll mi) {
	ll res = 1;
	for (; mi; mi >>= 1, x = x * x % P)
		if (mi & 1) res = res * x % P;
	return res;
}

int main() {
	read(n);
	for (int i = 1;i <= n; i++) read(a[i]);
	fac[0] = 1;
	for (int i = 1;i <= n; i++)
		fac[i] = fac[i-1] * i % P;
	inv[n] = fpw(fac[n], P - 2);
	for (int i = n - 1; i >= 1; i--) 
		inv[i] = inv[i+1] * (i + 1) % P;
	inv[0] = dp[0] = 1, sort(a + 1, a + n + 1);
	int j = 0;
	if (a[n-1] * 2 > a[n]) return write(0), 0;
	for (int i = 1;i <= n; i++) { 
		while (j < n && a[j+1] * 2 <= a[i]) j++;
		dp[i] = (A(n - 1, j) + pre[j] * inv[n - 1 - j]) % P;
		pre[i] = (pre[i-1] + dp[i] * fac[n - j - 2]) % P;
	} 
	write(dp[n]);
	return 0;
}

G. Death DBMS

套路模板题,链剖 + AC 自动姬,考场上竟然码出来了,我也是码农

jly 有更好写的 (Theta(nsqrt n)) 算法,考虑暴力跳 fail,每次跳 fail 当前串长度都会减少 1,由于空节点的的值事 -1,我们直接跳过去,由于长度不同的串最多有 (sqrt n) 个,所以复杂度正确。

const int N = 600050;
int ch[N][26], en[N], f[N], id[N], ed[N], cnt = 1;
char s[N];
inline void insert(char *s, int k) {
	int p = 1, len = strlen(s + 1); 
	for (int i = 1;i <= len; i++) {
		int c = s[i] - 'a';
		if (!ch[p][c]) ch[p][c] = ++cnt;
		p = ch[p][c];
	}
	en[k] = p, ed[p] = k;
}

int dep[N], siz[N], son[N], Top[N], dfn[N], num;
int h[N], ne[N<<1], to[N<<1], tot;
inline void add(int x, int y) { ne[++tot] = h[x], to[h[x] = tot] = y; }
void dfs(int x) {
	dep[x] = dep[f[x]] + (siz[x] = 1);
	for (int i = h[x], y; i; i = ne[i]) {
		dfs(y = to[i]), siz[x] += siz[y];
		if (siz[son[x]] < siz[y]) son[x] = y;
	}
}

void dfs2(int x, int topf) {
	Top[x] = topf, dfn[x] = ++num, id[num] = x;
	if (son[x]) dfs2(son[x], topf);
	for (int i = h[x], y; i; i = ne[i])
		if ((y = to[i]) != son[x]) dfs2(y, y);
}

void build(void) {
    queue<int> q;
    for (int i = 0;i < 26; i++)
        if (ch[1][i]) q.push(ch[1][i]), f[ch[1][i]] = 1;
        else ch[1][i] = 1;
    while (q.size()) {
        int x = q.front(); q.pop();
        if (x != 1) add(f[x], x);
        for (int i = 0;i < 26; i++) {
            if (!ch[x][i]) ch[x][i] = ch[f[x]][i];
            else q.push(ch[x][i]), f[ch[x][i]] = ch[f[x]][i];
        }
    }
}

#define ls p << 1
#define rs ls | 1
int mx[N<<2];

void build(int p, int l, int r) {
	mx[p] = -1;
	if (l == r) return mx[p] = ed[id[l]] ? 0 : -1, void();
	int mid = (l + r) >> 1;
	build(ls, l, mid), build(rs, mid + 1, r);
	mx[p] = max(mx[ls], mx[rs]);
}

void change(int p, int l, int r, int x, int c) {
	if (l == r) return mx[p] = c, void();
	int mid = (l + r) >> 1;
	if (x <= mid) change(ls, l, mid, x, c);
	else change(rs, mid + 1, r, x, c);
	mx[p] = max(mx[ls], mx[rs]);
}

void query(int p, int l, int r, int L, int R, int &ans) {
	if (L <= l && r <= R) return Mx(ans, mx[p]);
	int mid = (l + r) >> 1;
	if (L <= mid) query(ls, l, mid, L, R, ans);
	if (R > mid) query(rs, mid + 1, r, L, R, ans);
}

void query(int x, int &ans) {
	while (x) {
		query(1, 1, num, dfn[Top[x]], dfn[x], ans);
		x = f[Top[x]];
	}
}

struct Heap {
	priority_queue<int> A, B;
	void update(void) { while (B.size() && A.top() == B.top()) A.pop(), B.pop(); }
	int Top(void) { update(); return A.top(); }
	void del(int x) { B.push(x); }
	void push(int x) { A.push(x); }
}q[300005];

int pre[N], n, m;
int main() {
	read(n), read(m);
	for (int i = 1;i <= n; i++) 
		scanf ("%s", s + 1), insert(s, i), q[en[i]].push(0);
	build(), dfs(1), dfs2(1, 1), build(1, 1, num);
	for (int i = 1, x, y;i <= m; i++) {
		int op; read(op);
		if (op == 1) {
			read(x), read(y);
			q[en[x]].push(y), q[en[x]].del(pre[x]), pre[x] = y;
			change(1, 1, num, dfn[en[x]], q[en[x]].Top());
		}
		else {
			scanf ("%s", s + 1);
			int len = strlen(s + 1), nw = 1, ans = -1;
			for (int i = 1;i <= len; i++) {
				nw = ch[nw][s[i] - 'a'];
				query(nw, ans);
			}
			write(ans);
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/Hs-black/p/13890180.html