【CodeForces】CodeForces Global Round 1 题解

【比赛链接】

  • 点击打开连接

【题解链接】

  • 点击打开链接

**【A】**Parity

【思路要点】

  • b b b 的奇偶性讨论即可。
  • 时间复杂度 O ( k ) O(k) O(k)

【代码】

#include
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int main() {
	int k, n, x; read(k), read(n);
	bool ans = false;
	if (k & 1) {
		for (int i = 1; i <= n; i++) {
			read(x);
			ans ^= x % 2 == 1;
		}
	} else {
		for (int i = 1; i <= n; i++)
			read(x);
		ans = x % 2 == 1;
	}
	if (ans) puts("odd");
	else puts("even");
	return 0;
}

**【B】**Tape

【思路要点】

  • 在全部的 N − 1 N-1 N1 段间断位置中,选择最短的 N − k N-k Nk 段覆盖。
  • 时间复杂度 O ( N L o g N ) O(NLogN) O(NLogN)

【代码】

#include
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int n, m, k, a[MAXN], b[MAXN];
int main() {
	read(n), read(m), read(k);
	for (int i = 1; i <= n; i++)
		read(a[i]);
	for (int i = 2; i <= n; i++)
		b[i] = a[i] - a[i - 1] - 1;
	sort(b + 1, b + n + 1);
	int ans = a[n] - a[1] + 1;
	while (k >= 2) {
		ans -= b[n];
		n--, k--;
	}
	writeln(ans);
	return 0;
}

**【C】**Meaningless Operations

【思路要点】

  • f ( x ) f(x) f(x) 表示不小于 x x x 的,二进制表示每一位都是 1 1 1 的数。
  • f ( a ) ≠ a f(a)\ne a f(a)̸=a ,取 b = f ( a ) ⊕ a b=f(a)\oplus a b=f(a)a 即可将答案取至上界 f ( a ) f(a) f(a)
  • 否则, g c d ( a ⊕ b , a & b ) = g c d ( a − b , b ) gcd(a\oplus b,a\&b)=gcd(a-b,b) gcd(ab,a&b)=gcd(ab,b) ,问题即找到 a a a 最大的因数。
  • 时间复杂度 O ( Q A ) O(Q\sqrt{A}) O(QA )

【代码】

#include
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int main() {
	int q; read(q);
	while (q--) {
		int n; read(n);
		int tmp = 1;
		while (tmp < n) {
			tmp <<= 1;
			tmp += 1;
		}
		if (tmp == n) {
			int ans = 1;
			for (int i = 2; i * i <= n; i++)
				if (n % i == 0) {
					ans = n / i;
					break;
				}
			writeln(ans);
		} else writeln(tmp);
	}
	return 0;
}

**【D】**Jongmah

【思路要点】

  • 可以发现,相同的三个顺子同样可以表示为三个刻子,因此我们不妨假设相同的顺子至多存在 2 2 2 个。
  • 这样,我们就可以设计动态规划了,记 d p i , j , k dp_{i,j,k} dpi,j,k 表示考虑了所有以 1 , 2 , . . . , i 1,2,...,i 1,2,...,i 开头的顺子和这些牌对应的刻子,且 i − 1 i-1 i1 开头的顺子有 j   ( j ≤ 2 ) j\ (j≤2) j (j2) 个, i i i 开头的顺子有 k   ( k ≤ 2 ) k\ (k≤2) k (k2) 个的情况下,最多的牌型总数,转移时枚举 i + 1 i+1 i+1 开头的顺子个数即可。
  • 时间复杂度 O ( N + M ) O(N+M) O(N+M)

【代码】

#include
using namespace std;
const int MAXN = 1e6 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int cnt[MAXN], ans[MAXN];
int dp[MAXN][3][3];
int main() {
	int n, m; read(n), read(m);
	for (int i = 1; i <= n; i++) {
		int x; read(x);
		cnt[x]++;
	}
	memset(dp, -1, sizeof(dp));
	dp[1][0][0] = 0;
	for (int i = 1; i <= m; i++) {
		for (int j = 0; j <= 2; j++)
		for (int k = 0; k <= 2; k++) {
			if (dp[i][j][k] == -1) continue;
			int tmp = dp[i][j][k];
			if (j + k > cnt[i]) continue;
			for (int l = 0; l <= 2 && j + k + l <= cnt[i]; l++)
				chkmax(dp[i + 1][k][l], tmp + l + (cnt[i] - j - k - l) / 3);
		}
	}
	writeln(dp[m + 1][0][0]);
	return 0;
}

**【E】**Magic Stones

【思路要点】

  • 如果我们将序列 { a 1 , a 2 , a 3 , . . . , a n } \{a_1,a_2,a_3,...,a_n\} {a1,a2,a3,...,an} 描述为 { a 1 , a 2 − a 1 , a 3 − a 2 , . . . , a n − a n − 1 } \{a_1,a_2-a_1,a_3-a_2,...,a_n-a_{n-1}\} {a1,a2a1,a3a2,...,anan1} ,那么题目中给出的操作相当于交换了该差分数组中相邻的两位。
  • 因此,我们只需要比较两个数组的第一位和差分数组即可。
  • 时间复杂度 O ( N L o g N ) O(NLogN) O(NLogN)

【代码】

#include
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int n, a[MAXN], b[MAXN];
int main() {
	read(n);
	for (int i = 1; i <= n; i++)
		read(a[i]);
	for (int i = 1; i <= n; i++)
		read(b[i]);
	for (int i = n; i >= 2; i--) {
		a[i] -= a[i - 1];
		b[i] -= b[i - 1];
	}
	sort(a + 2, a + n + 1);
	sort(b + 2, b + n + 1);
	for (int i = 1; i <= n; i++)
		if (a[i] != b[i]) {
			puts("No");
			return 0;
		}
	puts("Yes");
	return 0;
}

**【F】**Nearest Leaf

【思路要点】

  • 离线询问,按照欧拉序遍历整棵树,我们可以用线段树维护出所有节点到当前所在节点的距离,以及该距离的区间最小值,延边移动时进行区间加减即可。
  • 时间复杂度 O ( N L o g N + Q L o g N ) O(NLogN+QLogN) O(NLogN+QLogN)

【代码】

#include
using namespace std;
const int MAXN = 5e5 + 5;
const int MAXLOG = 21;
const long long INF = 1e18;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
struct SegmentTree {
	struct Node {
		int lc, rc;
		ll tag, Min;
	} a[MAXN * 2];
	int n, size, root;
	void update(int root) {
		a[root].Min = min(a[a[root].lc].Min, a[a[root].rc].Min);
	}
	void build(int &root, int l, int r, ll *val) {
		root = ++size;
		if (l == r) {
			a[root].Min = val[l];
			return;
		}
		int mid = (l + r) / 2;
		build(a[root].lc, l, mid, val);
		build(a[root].rc, mid + 1, r, val);
		update(root);
	}
	void init(int x, ll *val) {
		n = x;
		root = size = 0;
		build(root, 1, n, val);
	}
	void pushdown(int root) {
		if (a[root].tag == 0) return;
		a[a[root].lc].tag += a[root].tag;
		a[a[root].lc].Min += a[root].tag;
		a[a[root].rc].tag += a[root].tag;
		a[a[root].rc].Min += a[root].tag;
		a[root].tag = 0;
	}
	void modify(int root, int l, int r, int ql, int qr, int val) {
		if (l == ql && r == qr) {
			a[root].tag += val;
			a[root].Min += val;
			return;
		}
		pushdown(root);
		int mid = (l + r) / 2;
		if (mid >= ql) modify(a[root].lc, l, mid, ql, min(qr, mid), val);
		if (mid + 1 <= qr) modify(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, val);
		update(root);
	}
	void modify(int l, int r, int val) {
		if (l > r) return;
		else modify(root, 1, n, l, r, val);
	}
	ll query(int root, int l, int r, int ql, int qr) {
		if (l == ql && r == qr) return a[root].Min;
		ll ans = INF;
		pushdown(root);
		int mid = (l + r) / 2;
		if (mid >= ql) chkmin(ans, query(a[root].lc, l, mid, ql, min(mid, qr)));
		if (mid + 1 <= qr) chkmin(ans, query(a[root].rc, mid + 1, r, max(mid + 1, ql), qr));
		return ans;
	}
	ll query(int l, int r) {
		if (l > r) return 0;
		else return query(root, 1, n, l, r);
	}
} ST;
int n, q, Max[MAXN];
ll sum[MAXN], ans[MAXN]; bool leaf[MAXN];
vector <pair <int, int> > a[MAXN];
vector <pair <pair <int, int>, int>> b[MAXN];
void dfs(int pos, int fa) {
	Max[pos] = pos;
	for (auto x : a[pos]) {
		sum[x.first] = sum[pos] + x.second;
		dfs(x.first, x.second);
		chkmax(Max[pos], Max[x.first]);
	}
}
void modify(int l, int r, int val) {
	ST.modify(l, r, -val);
	ST.modify(1, l - 1, val);
	ST.modify(r + 1, n, val);
}
void work(int pos, int fa) {
	modify(pos, Max[pos], fa);
	for (auto x : b[pos])
		ans[x.second] = ST.query(x.first.first, x.first.second);
	for (auto x : a[pos])
		work(x.first, x.second);
	modify(pos, Max[pos], -fa);
}
int main() {
	read(n), read(q);
	for (int i = 1; i <= n; i++)
		leaf[i] = true;
	for (int i = 2; i <= n; i++) {
		int x, y; read(x), read(y);
		a[x].emplace_back(i, y);
		leaf[x] = false;
	}
	dfs(1, 0);
	for (int i = 1; i <= n; i++)
		if (!leaf[i]) sum[i] = INF;
	for (int i = 1; i <= q; i++) {
		int pos, l, r;
		read(pos), read(l), read(r);
		b[pos].emplace_back(make_pair(l, r), i);
	}
	ST.init(n, sum);
	work(1, 0);
	for (int i = 1; i <= q; i++)
		writeln(ans[i]);
	return 0;
}

**【G】**Tree-Tac-Toe

【思路要点】

  • 显然,黑棋不会获胜。
  • 特殊处理 N ≤ 4 N≤4 N4 的情况。
  • N ≥ 5 N≥5 N5 ,若出现度数 ≥ 4 ≥4 4 的节点,则白胜。
  • 若出现度数 = 3 =3 =3 ,且其邻点中有至少 2 2 2 个不是叶子结点的节点,则白胜。
  • 若不存在上述两种情况,那么树的形态将会十分单一,即除去叶子节点后只有可能是一条长度至少为 2 2 2 的链,且只有链的两端挂有 1 1 1 2 2 2 个叶子结点。
  • 在这棵树上,若一个非叶节点已经存在一枚白子,则白胜。
  • 若挂有 2 2 2 个叶子结点的一端的某个叶子结点已经存在一枚白子,则白胜。
  • 剩余的情况即仅有挂有 1 1 1 个叶子结点的一端可能存在白子,或不存在白子,分类讨论链长的奇偶性即可,具体可见代码。
  • 时间复杂度 O ( N ) O(N) O(N)

【代码】

#include
using namespace std;
const int MAXN = 5e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int n; char s[MAXN];
vector <int> a[MAXN];
void solve() {
	if (n <= 2) {
		puts("Draw");
		return;
	}
	if (n == 3) {
		int cnt = 0;
		for (int i = 1; i <= n; i++)
			cnt += s[i] == 'W';
		if (cnt >= 2) puts("White");
		else puts("Draw");
		return;
	}
	if (n == 4) {
		int cnt = 0; bool flg = false;
		for (int i = 1; i <= n; i++) {
			cnt += s[i] == 'W';
			if (a[i].size() == 3) flg = true;
		}	
		if (flg) {
			if (cnt >= 1) puts("White");
			else puts("Draw");
		} else {
			for (int i = 1; i <= n; i++)
				if (s[i] == 'W' && a[i].size() >= 2) {
					puts("White");
					return;
				}
			puts("Draw");
		}
		return;
	}
	int cnt = 0, tot = 0, spe = 0;
	for (int i = 1; i <= n; i++) {
		if (s[i] == 'W') {
			tot++;
			if (a[i].size() >= 2 || a[a[i][0]].size() >= 3) {
				puts("White");
				return;
			}
		}
		if (a[i].size() >= 2) cnt++;
		if (a[i].size() >= 4) {
			puts("White");
			return;
		}
		if (a[i].size() >= 3) {
			int cmt = 0; spe++;
			for (auto x : a[i])
				if (a[x].size() >= 2) cmt++;
			if (cmt >= 2) {
				puts("White");
				return;
			}
		}
	}
	assert(cnt >= 2 && tot <= 2);
	if (tot && spe && cnt % 2 == 1) {
		puts("White");
		return;
	}
	if (tot >= 2 && cnt % 2 == 1) {
		puts("White");
		return;
	}
	if (spe >= 2 && cnt % 2 == 1) {
		puts("White");
		return;
	}
	puts("Draw");
}
int main() {
	int T; read(T);
	while (T--) {
		read(n);
		for (int i = 1; i <= n; i++)
			a[i].clear();
		for (int i = 1; i <= n - 1; i++) {
			int x, y; read(x), read(y);
			a[x].push_back(y);
			a[y].push_back(x);
		}
		scanf("\n%s", s + 1);
		solve();
	}
	return 0;
}

**【H】**Modest Substrings

【思路要点】

  • 我们可以将 l l l r r r 之间的所有整数描述为大约 ( ∣ l ∣ + ∣ r ∣ ) ∗ 10 (|l|+|r|)*10 (l+r)10 个带有通配符后缀的字符串的并集,例如 l = 10 , r = 34 l=10,r=34 l=10,r=34 时,该集合即为 { 1 ∗ , 2 ∗ , 30 , 31 , 32 , 33 , 34 } \{1*,2*,30,31,32,33,34\} {1,2,30,31,32,33,34} 。注意任意一个字符串只会匹配集合中至多一个元素。
  • 将所有集合内的字符串除去通配符后缀后建立 A C AC AC 自动机,再在每一个节点上记录该节点具有的通配符后缀的长度集合。由于字符串集合的特殊性,该 A C AC AC 自动机的点数依然是 O ( ( ∣ l ∣ + ∣ r ∣ ) ∗ 10 ) O((|l|+|r|)*10) O((l+r)10) 级别的。
  • 考虑用动态规划解题,记 d p i , j dp_{i,j} dpi,j 表示当前字符串长度为 i i i ,在 A C AC AC 自动机上匹配到的节点为 j j j 的最优答案的值。转移时枚举出边 k k k ,记到达的点为 d e s t dest dest ,则将 d p i , j dp_{i,j} dpi,j 加上 d e s t dest dest f a i l fail fail 链上所有节点长度在 N − i − 1 N-i-1 Ni1 以内的通配符后缀的个数转移到 d p i + 1 , d e s t dp_{i+1,dest} dpi+1,dest
  • 时间复杂度 O ( ( ∣ l ∣ + ∣ r ∣ ) ∗ 1 0 2 ∗ N ) O((|l|+|r|)*10^2*N) O((l+r)102N)

【代码】

#include
using namespace std;
const int MAXN = 20005;
const int MAXM = 2005;
const int MAXL = 805;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
struct ACAutomaton {
	struct Node {
		int child[10];
		int fail, sum[MAXL];
	} a[MAXN];
	int lenl, lenr, root, size, dp[MAXM][MAXN];
	char l[MAXN], r[MAXN];
	void buildfail() {
		int l = 0, r = -1;
		static int q[MAXN];
		a[root].fail = root;
		for (int i = 0; i <= 9; i++)
			if (a[root].child[i]) {
				a[a[root].child[i]].fail = root;
				q[++r] = a[root].child[i];
			} else a[root].child[i] = root;
		while (l <= r) {
			int pos = q[l++];
			for (int i = 0; i <= lenr; i++)
				a[pos].sum[i] += a[a[pos].fail].sum[i];
			for (int i = 0; i <= 9; i++)
				if (a[pos].child[i]) {
					a[a[pos].child[i]].fail = a[a[pos].fail].child[i];
					q[++r] = a[pos].child[i];
				} else a[pos].child[i] = a[a[pos].fail].child[i];
		}
		for (int i = 0; i <= size; i++)
		for (int j = 1; j <= lenr; j++)
			a[i].sum[j] += a[i].sum[j - 1];
	}
	void init() {
		scanf("%s", l + 1);
		scanf("%s", r + 1);
		lenl = strlen(l + 1);
		lenr = strlen(r + 1);
		root = size = 0;
		if (lenl == lenr) {
			bool flg = false;
			for (int i = 1, posl = root, posr = root; i <= lenl; posl = a[posl].child[l[i] - '0'], posr = a[posr].child[r[i] - '0'], i++) {
				if (flg) {
					for (int j = l[i] - '0'; j <= 9; j++) {
						if (a[posl].child[j] == 0) a[posl].child[j] = ++size;
						if (i == lenl || j != l[i] - '0') a[a[posl].child[j]].sum[lenl - i] = 1;
					}
					for (int j = 0; j <= r[i] - '0'; j++) {
						if (a[posr].child[j] == 0) a[posr].child[j] = ++size;
						if (i == lenl || j != r[i] - '0') a[a[posr].child[j]].sum[lenl - i] = 1;
					}
				} else {
					for (int j = l[i] - '0'; j <= r[i] - '0'; j++) {
						if (a[posl].child[j] == 0) a[posl].child[j] = ++size;
						if (i == lenl || j != l[i] - '0' && j != r[i] - '0') a[a[posl].child[j]].sum[lenl - i] = 1;
					}
				}
				flg |= l[i] != r[i];
			}
		} else {
			for (int i = 1, pos = root; i <= lenl; pos = a[pos].child[l[i] - '0'], i++) {
				for (int j = l[i] - '0'; j <= 9; j++) {
					if (a[pos].child[j] == 0) a[pos].child[j] = ++size;
					if (i == lenl || j != l[i] - '0') a[a[pos].child[j]].sum[lenl - i] = 1;
				}
			}
			for (int i = 1, pos = root; i <= lenr; pos = a[pos].child[r[i] - '0'], i++) {
				for (int j = 0 + (i == 1); j <= r[i] - '0'; j++) {
					if (a[pos].child[j] == 0) a[pos].child[j] = ++size;
					if (i == lenr || j != r[i] - '0') a[a[pos].child[j]].sum[lenr - i] = 1;
				}
			}
			for (int i = lenl + 1; i <= lenr - 1; i++) {
				for (int j = 1; j <= 9; j++) {
					if (a[root].child[j] == 0) a[root].child[j] = ++size;
					a[a[root].child[j]].sum[i - 1] = 1;
				}
			}
		}
		buildfail();
	}
	void query(int n) {
		memset(dp, -1, sizeof(dp)), dp[0][0] = 0;
		for (int i = 0; i <= n - 1; i++)
		for (int j = 0; j <= size; j++) {
			if (dp[i][j] == -1) continue;
			int tmp = dp[i][j];
			for (int k = 0; k <= 9; k++) {
				int dest = a[j].child[k];
				chkmax(dp[i + 1][dest], tmp + a[dest].sum[min(n - i - 1, lenr)]);
			}
		}
		int ans = 0;
		for (int i = 0; i <= size; i++)
			chkmax(ans, dp[n][i]);
		printf("%d\n", ans);
		bool opt[MAXM][MAXN];
		for (int i = 0; i <= size; i++)
			if (dp[n][i] == ans) opt[n][i] = true;
		for (int i = n - 1; i >= 0; i--)
		for (int j = 0; j <= size; j++) {
			if (dp[i][j] == -1) continue;
			int tmp = dp[i][j];
			for (int k = 0; k <= 9; k++) {
				int dest = a[j].child[k];
				if (opt[i + 1][dest] && dp[i + 1][dest] == tmp + a[dest].sum[min(n - i - 1, lenr)]) opt[i][j] = true; 
			}
		}
		assert(opt[0][0]);
		int pos = 0;
		for (int i = 1; i <= n; i++) {
			int tmp = dp[i - 1][pos];
			for (int j = 0; j <= 9; j++) {
				int dest = a[pos].child[j];
				if (opt[i][dest] && dp[i][dest] == tmp + a[dest].sum[min(n - i, lenr)]) {
					printf("%d", j);
					pos = a[pos].child[j];
					break;
				}
			}
		}
		printf("\n");
	}
} ACAM;
int main() {
	ACAM.init();
	int n; read(n);
	ACAM.query(n);
	return 0;
}

你可能感兴趣的:(【类型】做题记录,【比赛】CodeForces,【OJ】CodeForces,【算法】贪心,【算法】动态规划,【数据结构】AC自动机,【算法】博弈论,【算法】分类讨论,【算法】构造与证明,【数据结构】线段树,【算法】离线操作,【算法】差分与前缀和思想)