Codeforces Global Round 1题解报告

Codeforces Global Round 1题解报告

A. Parity

题意

模2意义下的秦九韶。

题解

模2意义下的秦九韶。

代码
#include<bits/stdc++.h>
int ri() {
	char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f  = -1;
	for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int main() {
	int b = ri(), k = ri(), n = 0;
	for(int i = 1;i <= k; ++i)
		n = (n * b + ri()) & 1;
	puts(n ? "odd" : "even");
	return 0;
}

B. Tape

题意

给你一条长度为 m m m的绳子,上面有 n n n处断点,利用要最多 k k k条胶带覆盖这些断点,最小化长度。

题解

取前 k k k大的断点间隔即可。记得掐头去尾。

代码
#include<bits/stdc++.h>
const int N = 1e5 + 10;
int ri() {
	char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f  = -1;
	for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int n, m, k, b[N];
int main() {
	n = ri(); m = ri(); k = ri();
	for(int i = 0;i < n; ++i)
		b[i] = ri();
	int All = b[n - 1] - b[0] + 1, Sum = 0;
	for(int i = n - 1; i; --i)
		b[i] -= b[i - 1] + 1;
	std::sort(b + 1, b + n);
	for(int i = 1; i < k && i < n; ++i)
		Sum += b[n - i];
	printf("%d\n", All - Sum);
	return 0;
}

C. Meaningless Operations

题意

f ( a ) = max ⁡ 0 &lt; b &lt; a g c d ( a ⊕ b , a &amp; b ) f(a)= \max_{0&lt;b&lt;a}gcd(a⊕b,a \&amp; b) f(a)=max0<b<agcd(ab,a&b),多组询问 a a a,求 f ( a ) f(a) f(a)

题解

如果 a ≠ 2 n − 1 a\neq 2^n-1 a̸=2n1,将 a a a拆位考虑, 1 ⊕ 0 = 1 , 1 &amp; 0 = 0 ; 0 ⊕ 1 = 1 , 0 &amp; 1 = 0 1⊕0=1,1 \&amp; 0 = 0;0⊕1=1,0\&amp; 1 = 0 10=1,1&0=0;01=1,0&1=0,这样的话构造出来的就是 a ⊕ b = 111 ⋯ 1 1 ( 2 ) , a &amp; b = 0 a⊕b=111\cdots 11_{(2)},a\&amp; b = 0 ab=11111(2),a&b=0 f ( a ) f(a) f(a)在此时最大。
可是当 a = 2 n − 1 a= 2 ^n-1 a=2n1是,构造出来的 b = 0 b=0 b=0,不符合题意。考场上当然直接打表啦。
题解给的方法是,此时相当于是 g c d ( 2 x − 1 − b , b ) = g c d ( 2 x − 1 , b ) gcd(2^x-1-b,b)=gcd(2^x-1,b) gcd(2x1b,b)=gcd(2x1,b),就是找 2 x − 1 2^x-1 2x1的最大因子。

代码
#include<bits/stdc++.h>
int ri() {
	char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f  = -1;
	for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int bin[30];
const int biao[] = {1,1,5,1,21,1,85,73,341,89,1365,1,5461,4681,21845,1,87381,1,349525,299593,1398101,178481,5592405,1082401};
int main() {
	bin[0] = 1;
	for(int i = 1;i <= 25; ++i)
		bin[i] = bin[i - 1] << 1;
	int q = ri();
	for(;q--;) {
		int a = ri(), mx;
		for(int i = 24;~i; --i)
			if(a >= bin[i]) {
				mx = i + 1;
				break;
			}
		if(bin[mx] - 1 == a) printf("%d\n", biao[mx - 2]);
		else printf("%d\n", bin[mx] - 1);
	}
	return 0;
}

D. Jongmah

题意

从一个序列里面挑出尽量多的三元组,满足要么三个数相同要么三个数连续。

题解

考虑如果某个连续型三元组超过了三个,可以直接转化成相同型的三元组,所以存在最优解使得完全相同的连续型三元组不超过两个,于是同一种数至多有六个用于组成连续型三元组。排序之后状压前面两个数值的用于组成连续三元组的情况即可。

代码
#include<bits/stdc++.h>
const int N = 1e6 + 10;
int ri() {
	char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f  = -1;
	for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int n, m, Ans, c[N], dp[2][49];
void Up(int &a, int b) {a = std::max(a, b);}
int main() {
	n = ri(); m = ri();
	for(int i = 1;i <= n; ++i)
		++c[ri()];
	memset(dp, -0x3f, sizeof(dp));
	dp[0][0] = 0; int *g = dp[0], *f = dp[1];
	for(int i = 1;i <= m; ++i, std::swap(f, g)) {
		memset(f, -0x3f, sizeof(dp[0]));
		for(int c1 = 0;c1 < 7; ++c1)
			for(int c2 = 0; c2 < 7; ++c2) {
				int mx = std::min(std::min(c1, std::min(c2, c[i])), 2);
				for(int t = 0;t <= mx; ++t) {
					int re = c[i] - t;
					if(re < 3) {
						Up(f[(c2 - t) * 7 + re], g[c1 * 7 + c2] + t);
					}
					else if(re < 6) {
						Up(f[(c2 - t) * 7 + re], g[c1 * 7 + c2] + t);
						Up(f[(c2 - t) * 7 + re - 3], g[c1 * 7 + c2] + t + 1);
					}
					else {
						int rre = re % 3 + 3, tm = re / 3 - 1;
						Up(f[(c2 - t) * 7 + rre], g[c1 * 7 + c2] + t + tm);
						Up(f[(c2 - t) * 7 + rre - 3], g[c1 * 7 + c2] + t + tm + 1);
					}
				}
			}
	}
	int mx = 0;
	for(int i = 0;i < 49; ++i)
		mx = std::max(mx, g[i]);
	printf("%d\n", mx);
	return 0;
}

E. Magic Stones

题意

给一个序列,可以进行任意进行以下操作, c i ′ = c i − 1 + c i + 1 − c i c_i&#x27;=c_{i-1}+c_{i+1}-c_i ci=ci1+ci+1ci,求是否可以使一个序列变成另一个序列。

题解

哎这种题就是想到就会,没想到就没救的那种题。早知道就直接跳了,害得我那么简单的 F F F没做。
差分完之后这个操作就是交换。。。然后直接排序即可。

代码
#include<bits/stdc++.h>
const int N = 1e5 + 10;
int ri() {
	char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f  = -1;
	for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int n, c[N], t[N];
int main() {
	n = ri();
	for(int i = 0;i < n; ++i)
		c[i] = ri();
	for(int i = 0;i < n; ++i)
		t[i] = ri();
	if(c[0] != t[0]) return puts("No"), 0;
	for(int i = n - 1; i; --i)
		c[i] -= c[i - 1];
	for(int i = n - 1; i; --i)
		t[i] -= t[i - 1];
	std::sort(c + 1, c + n);
	std::sort(t + 1, t + n);
	for(int i = 1;i < n; ++i)
		if(t[i] != c[i])
			return puts("No"), 0;
	puts("Yes");
	return 0;
}

F. Nearest Leaf

题意

给你一个按照dfs序建立的树,问距离某个点dfs序在某个区间的距离最近的叶子。

题解

出栈入栈序记一下,询问离线,进入子树的时候把边的贡献在子树内减掉,在子树外加上,出子树的时候回溯一下即可。询问用线段树。

代码

尝试了一下zkw,感觉不错。

#include<bits/stdc++.h>
const int N = 5e5 + 10, Nt = 1 << 20;
typedef long long LL;
const LL inf = 1e18;
int ri() {
	char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f  = -1;
	for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int l[N], r[N], pr[N], nx[N], w[N], m, n, Q;
LL T[Nt], ans[N], D[N];
struct Data {int l, r, id;};
std::vector<Data>q[N];
void Dfs1(int u) {
	l[u] = r[u] = u;
	for(int i = pr[u];i; i = nx[i]) {
		D[i] = D[u] + w[i];
		Dfs1(i);
		l[u] = std::min(l[u], l[i]);
		r[u] = std::max(r[u], r[i]);
	}
	if(!pr[u])
		T[u + m] = D[u];
	else
		T[u + m] = inf;
}
void Up(int s) {LL A = std::min(T[s], T[s ^ 1]); T[s] -= A; T[s ^ 1] -= A; T[s >> 1] += A;}
void Add(int s, int t, int x) {
	if(s > t) return ;s += m, t += m;
	if(s == t) {
		for(T[s] += x;s > 1; s >>= 1) Up(s);
		return ;
	}
	for(T[s] += x, T[t] += x;s ^ t ^ 1; s >>= 1, t >>= 1) {
		if(~s & 1)
			T[s ^ 1] += x;
		if(t & 1)
			T[t ^ 1] += x;
		Up(s); Up(t);
	}
	for(;s > 1; s >>= 1) 
		Up(s);
}
LL Query(int s, int t) {
	LL l = 0, r = 0;
	if(s == t) {
		for(s += m;s; s >>= 1) l += T[s];
		return l;
	}
	for(s += m, t += m;s ^ t ^ 1; s >>= 1, t >>= 1) {
		l += T[s]; r += T[t];
		if(~s & 1)
			l = std::min(l, T[s ^ 1]);
		if(t & 1)
			r = std::min(r, T[t ^ 1]);
	}
	l = std::min(l + T[s], r + T[t]);
	for(;s >>= 1;) l += T[s];
	return l;
}
void Dfs2(int u) {
	for(Data x : q[u])
		ans[x.id] = Query(x.l, x.r);
	for(int i = pr[u]; i; i = nx[i]) {
		Add(l[i], r[i], -w[i]);
		Add(1, l[i] - 1, w[i]);
		Add(r[i] + 1, n, w[i]);
		Dfs2(i);
		Add(l[i], r[i], w[i]);
		Add(1, l[i] - 1, -w[i]);
		Add(r[i] + 1, n, -w[i]);
	}
}
int main() {
	n = ri(); Q = ri();
	for(m = 1;m <= n + 1; m <<= 1) ;
	for(int i = 2, u;i <= n; ++i) 
		u = ri(), nx[i] = pr[u], pr[u] = i, w[i] = ri();
	Dfs1(1);
	for(int i = m - 1; i; --i)
		T[i] = std::min(T[i << 1], T[i << 1 | 1]), T[i << 1] -= T[i], T[i << 1 | 1] -= T[i];
	for(int i = 1;i <= Q; ++i) {
		int v = ri(), l = ri(), r = ri();
		q[v].push_back((Data){l, r, i});
	}
	Dfs2(1);
	for(int i = 1;i <= Q; ++i)
		printf("%lld\n", ans[i]);
	return 0;
}

G. Tree-Tac-Toe

题意

树上三子棋,初始有白点。

题解

考后自己yy了一个神奇的做法。首先显然不可能黑胜,接下来大概就是大分类一下有白点的情况,然后枚举放的第一个白点和黑点的位置,巨烦。
看了一下标解我惊了。
想考虑没有白点的情况。如果某个点度数大于3,直接白胜,考虑度数为3的点,如果与它相邻有超过两个点不是叶子,那么白胜。剩下的情况只有以下几种:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
后两种都只能平局,第一种判奇偶性即可(白棋间隔着放)

代码
#include<bits/stdc++.h>
const int N = 1e6 + 10;
int ri() {
	char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f  = -1;
	for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
std::vector<int>G[N];
void add(int u, int v) {G[u].push_back(v);}
void adds(int u, int v) {add(u, v); add(v, u);}
int tot; char s[N];
bool Work() {
	for(int i = 1;i <= tot; ++i)
		G[i].clear();
	int n = tot = ri(), lf = 0;
	for(int i = 2;i <= n; ++i)
		adds(ri(), ri());
	scanf("%s", s + 1);
	for(int i = 1;i <= n; ++i)
		if(s[i] == 'W') 
			adds(i, ++tot), adds(tot, tot + 1), adds(tot, tot + 2), tot += 2;
	for(int i = 1;i <= tot; ++i)
		if(G[i].size() >= 4) return true;
		else if(G[i].size() == 1) ++lf;
	for(int i = 1;i <= tot; ++i) 
	if(G[i].size() == 3) {
		int cnt = 0;
		for(int j = 0;j < 3; ++j)
			cnt += G[G[i][j]].size() >= 2;
		if(cnt >= 2) return true;
	}
	if(lf <= 3) return false;
	return tot & 1;
}
int main() {
	for(int T =ri();T--;)
		puts(Work() ? "White" : "Draw");
	return 0;
}

H. Modest Substrings

题意

构造一个由数字构成的长度为 n n n的字符串,最大化其“合适的”子串的个数。其中“合适”定义为:1.没有前导0;2.其数值在 l , r l,r l,r之间

题解

有一种神奇的操作叫做“正则表达式”。
我们考虑如何表示数值 120 − 129 120-129 120129
一种简单的方式是列举 120 − 129 120 -129 120129之间的所有数值。
但是正则表达式中,有一个符号 [ a − b ] [a-b] [ab],表示匹配单个字符,这个字符可以是 a − b a-b ab中的任何字符。
比如 [ 0 − 9 ] [0-9] [09]就可以匹配 0 − 9 0-9 09十个字符。
于是我们可以用 12 [ 0 − 9 ] 12[0-9] 12[09]来表示 120 − 129 120-129 120129
那么如何表示 120000 − 129999 120000-129999 120000129999?
一种办法是, 12 [ 0 − 9 ] [ 0 − 9 ] [ 0 − 9 ] [ 0 − 9 ] 12[0-9][0-9][0-9][0-9] 12[09][09][09][09]
但是这样要重复 4 4 4次,很烦。
正则表达式有一种神奇的方法,就是 x { e } x\{e\} x{e},表示将 x x x单位字符重复 e e e次。
于是 120000 − 129999 120000-129999 120000129999就变成了 12 [ 0 − 9 ] { 4 } 12[0-9]\{4\} 12[09]{4}
这个时候考虑如何表示在 l l l r r r之间的所有字符。
l l l a 1 a 2 ⋯ a n a_1a_2\cdots a_n a1a2an
那么 x ≥ l x\ge l xl 可以表示为
a 1 ⋯ a k B [ 0 − 9 ] { n − k } ( B &gt; a k ) a_1\cdots a_kB[0-9]\{n-k\}(B&gt; a_k) a1akB[09]{nk}(B>ak)
[ 1 − 9 ] [ 0 − 9 ] { e } ( e ≥ n ) [1-9][0-9]\{e\}(e\ge n) [19][09]{e}(en)
也就是,考虑如果某个和 l l l位数相同的数,那么我们可以贴着 l l l走一段,然后再下一位放一个大于当前位的数,然后再瞎放,再加上位数大于 l l l的数。
l l l r r r两个限制加上去的处理方法是类似的。
我们发现由于 [ 0 − 9 ] { e } [0-9]\{e\} [09]{e}的使用,大大缩减了合法数的量,事实上这样的状态不超过 O ( 80 0 2 ) O(800^2) O(8002),因为只需要扔到 T r i e Trie Trie树里,平方是因为 { e } \{e\} {e}的个数最大是 800 800 800
我们发现其实 [ 0 − 9 ] { e } [0-9]\{e\} [09]{e}这个结构很特殊,我们称之为“通配符”,对于每个 T r i e Trie Trie,我们直接在每个节点上记录一个数组来表示这个符号即可。
剩下来考虑匹配。
我们发现任意一个合法的串都会切仅会被一个上面的正则表达式包含。
f i , u f_{i,u} fi,u表示放了 i i i个数,走到了 T r i e Trie Trie上的 u u u
我们发现,当前节点的所有 e e e不大于 n − i n-i ni的通配符都会对答案产生 1 1 1的贡献。因为不管后面放什么都一定会匹配到。同时,对这棵 T r i e Trie Trie建立 A C AC AC自动机。 f a i l fail fail链上的所有点的通配符都会对答案产生 1 1 1的贡献。
于是我们预处理 f a i l fail fail链,把儿子的贡献加到父亲上,然后跑一遍 D p Dp Dp即可。
神奇!

代码
#include<bits/stdc++.h>
const int N = 2e4 + 10, M = 2e3 + 5;
int n, m, sz, len, ch[N][10], q[N], fa[N], g[N][M], f[M][N];
bool ok[M][N]; char s[M], t[M];
bool Can(int i, int u, int k) {return f[i][u] == f[i + 1][ch[u][k]] - g[ch[u][k]][len - i - 1];}
int Tra(int u, int x) {return ch[u][x] ?: ch[u][x] = ++sz;}
void Up(int &a, int b) {a = std::max(a, b);}
int main() {
	scanf("%s", s + 1); n = strlen(s + 1);
	scanf("%s", t + 1); m = strlen(t + 1);
	scanf("%d", &len);
	for(int i = 1;i <= n; ++i) s[i] -= '0';
	for(int i = 1;i <= m; ++i) t[i] -= '0';
	int u = 0, v = 0;
	if(n == m) {
		for(int i = 1; i <= n; ++i) {
			if(u == v)
				for(int j = s[i] + 1; j < t[i]; ++j)
					++g[Tra(u, j)][n - i];
			else {
				for(int j = s[i] + 1; j < 10; ++j)
					++g[Tra(u, j)][n - i];
				for(int j = 0;j < t[i]; ++j)
					++g[Tra(v, j)][n - i];
			}
			u = Tra(u, s[i]); v = Tra(v, t[i]);
		}
		++g[u][0]; if(u != v) ++g[v][0];
	}
	else {
		for(int i = 1;i <= n; ++i) {
			for(int j = s[i] + 1; j < 10; ++j)
				++g[Tra(u, j)][n - i];
			u = Tra(u, s[i]);
		}
		for(int i = 1;i <= m;++i) {
			for(int j = 0;j < t[i]; ++j)
				++g[Tra(v, j)][m - i];
			v = Tra(v, t[i]);
		}
		++g[u][0]; ++g[v][0];
		for(int i = n + 1; i < m; ++i)
			for(int j = 1;j < 10; ++j)
				++g[Tra(0, j)][i - 1];
	}
	int L = 1, R = 0;
	ch[0][0] = 0;
	for(int i = 0;i < 10; ++i)
		if(ch[0][i])
			q[++R] = ch[0][i];
	for(int u = q[L];L <= R; u = q[++L]) {
		for(int i = 0;i < 10; ++i) {
			int &v = ch[u][i];
			if(!v) v = ch[fa[u]][i];
			else fa[v] = ch[fa[u]][i], q[++R] = v;
		}
		for(int i = 0;i <= m; ++i)
			g[u][i] += g[fa[u]][i];
	}
	for(int i = 1;i <= sz; ++i)
		for(int j = 1;j <= len; ++j)
			g[i][j] += g[i][j - 1];
	std::memset(f, -1, sizeof(f)); 
	f[0][0] = 0;
	for(int i = 0;i <= len; ++i)
		for(int j = 0;j <= sz; ++j)
			if(~f[i][j]) {
				f[i][j] += g[j][len - i];
				for(int k = 0;k < 10; ++k)
					Up(f[i + 1][ch[j][k]], f[i][j]);
			}
	int ans = 0;
	for(int i = 0;i <= sz; ++i)
		Up(ans, f[len][i]);
	for(int i = 0;i <= sz; ++i)
		if(ans == f[len][i])
			ok[len][i] = 1;
	for(int i = len - 1; ~i; --i)
		for(int u = 0;u <= sz; ++u)
			for(int k = 0;k < 10; ++k)
				if(Can(i, u, k))
					ok[i][u] |= ok[i + 1][ch[u][k]];
	printf("%d\n", ans);
	for(int i = 0, u = 0;i < len; ++i)
		for(int j = 0;j < 10; ++j)
			if(Can(i, u, j) && ok[i + 1][ch[u][j]]) {
				putchar(j + '0'); u = ch[u][j]; break;
			}
	return puts(""), 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值