【CodeForces】CodeForces Round #400 (Div. 1 + Div. 2) 题解

【比赛链接】

  • 点击打开连接

【题解链接】

  • 点击打开链接

**【A】**A Serial Killer

【思路要点】

  • 维护两个字符串模拟。
  • 时间复杂度 O ( N ) O(N) O(N)

【代码】

#include
using namespace std;
const int MAXN = 100005;
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("");
}
string a, b, c;
int main() {
	cin >> a >> b;
	int n; cin >> n;
	cout << a << ' ' << b << endl;
	for (int i = 1; i <= n; i++) {
		cin >> c;
		if (a == c) cin >> a;
		else cin >> b;
		cout << a << ' ' << b << endl;
	}
	return 0;
}

**【B】**Sherlock and his girlfriend

【思路要点】

  • N ≤ 2 N≤2 N2,将所有数涂为一种颜色。
  • 否则,将质数涂为一种颜色,非质数涂为一种颜色。
  • 时间复杂度 O ( N N ) O(N\sqrt{N}) O(NN )

【代码】

#include
using namespace std;
const int MAXN = 100005;
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("");
}
bool prime(int x) {
	for (int i = 2; i * i <= x; i++)
		if (x % i == 0) return false;
	return true;
}
int main() {
	int n; read(n);
	if (n <= 2) {
		printf("%d\n", 1);
		for (int i = 1; i <= n; i++)
			printf("%d ", 1);
		return 0;
	}
	printf("%d\n", 2);
	for (int i = 1; i <= n; i++)
		printf("%d ", 1 + prime(i + 1));
	return 0;
}

**【C】**Molly’s Chemicals

【思路要点】

  • 枚举可能的序列和(至多 O ( L o g K ) O(LogK) O(LogK)种)。
  • 枚举右端点,维护一个以前缀和为下标的左端点map,查询对应合法的左端点的个数即可。
  • 时间复杂度 O ( N L o g N L o g V ) O(NLogNLogV) O(NLogNLogV)

【代码】

#include
using namespace std;
const int MAXN = 100005;
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];
long long ans, k, s[MAXN];
long long calc(long long x) {
	static map <long long, int> mp;
	long long ans = 0; mp.clear();
	for (int i = 1; i <= n; i++) {
		mp[s[i - 1]]++;
		ans += mp[s[i] - x];
	}
	return ans;
}
int main() {
	read(n), read(k);
	for (int i = 1; i <= n; i++)
		read(a[i]), s[i] = s[i - 1] + a[i];
	long long now = 1;
	while (abs(now) <= 1e15) {
		ans += calc(now);
		now = now * k;
		if (now == 1) break;
	}
	writeln(ans);
	return 0;
}

**【D】**The Door Problem

【思路要点】

  • 考虑每一扇门,若其原本处于开启状态,其对应的两个开关应该开闭状态相同;否则其对应的两个开关应该开闭状态不同。
  • 将开关的开闭看作变量,容易得到一个2-SAT的解法。
  • 注意到本题中所建出的图为无向图,我们只需要用并查集简单维护即可。
  • 时间复杂度 O ( N + M ) O(N+M) O(N+M)

【代码】

#include
using namespace std;
const int MAXN = 2e5 + 5;
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, f[MAXN], r[MAXN], point[MAXN][2];
vector <int> p[MAXN];
int F(int x) {
	if (f[x] == x) return x;
	else return f[x] = F(f[x]);
}
int main() {
	read(n), read(m);
	for (int i = 1; i <= n; i++)
		read(r[i]);
	for (int i = 1; i <= m; i++) {
		int x; read(x);
		for (int j = 1; j <= x; j++) {
			int pos; read(pos);
			p[pos].push_back(i);
		}
	}
	int tot = 0;
	for (int i = 1; i <= m; i++) {
		point[i][0] = ++tot;
		point[i][1] = ++tot;
	}
	for (int i = 1; i <= tot; i++)
		f[i] = i;
	for (int i = 1; i <= n; i++) {
		int x = p[i][0];
		int y = p[i][1];
		if (r[i] == 0) {
			f[F(point[x][0])] = F(point[y][1]);
			f[F(point[x][1])] = F(point[y][0]);
		} else {
			f[F(point[x][0])] = F(point[y][0]);
			f[F(point[x][1])] = F(point[y][1]);
		}
	}
	for (int i = 1; i <= m; i++)
		if (F(point[i][0]) == F(point[i][1])) {
			printf("NO\n");
			return 0;
		}
	printf("YES\n");
	return 0;
}

**【E】**The Holmes Children

【思路要点】

  • 不难发现 f = ϕ , g = 1 ∗ f = i d f=\phi,g=1*f=id f=ϕ,g=1f=id
  • 题目本质上是对 N N N求了 ⌊ K + 1 2 ⌋ \lfloor\frac{K+1}{2}\rfloor 2K+1次欧拉函数。
  • 注意到奇数求欧拉函数后会变为偶数,而偶数求欧拉函数后会减半,所以 N N N在求 O ( L o g N ) O(LogN) O(LogN)次欧拉函数后就会变成1。
  • 变成1后终止该过程即可。
  • 注意输出时对 1 0 9 + 7 10^9+7 109+7取模。
  • 时间复杂度 O ( N L o g N ) O(\sqrt{N}LogN) O(N LogN)

【代码】

#include
using namespace std;
const int MAXN = 100005;
const int P = 1e9 + 7;
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("");
}
long long phi(long long x) {
	long long ans = x;
	for (int i = 2; 1ll * i * i <= x; i++) {
		if (x % i == 0) {
			ans = ans / i * (i - 1);
			while (x % i == 0) x /= i;
		}
	}
	if (x != 1) ans = ans / x * (x - 1);
	return ans;
}
int main() {
	long long n, k;
	read(n), read(k);
	if (k % 2 == 1) k = (k + 1) / 2;
	else k /= 2;
	while (k--) {
		n = phi(n);
		if (n == 1) break;
	}
	writeln(n % P);
	return 0;
}

**【F】**Sherlock’s bet to Moriarty

【思路要点】

  • 显然如果将题目中的多边形转成对偶图,那么会得到一棵树。
  • 对这棵树进行树分治,每次在分治重心上标上其在点分树上的深度即是一组符合条件的解。
  • 时间复杂度 O ( N L o g N ) O(NLogN) O(NLogN)
  • 关于实现上的一些细节:
  • 我们当然可以使用传统的平面图转对偶图的方法来解题,但我们也可以利用本题的特殊性得到一个 编程复杂度更低的做法:对所有边按照两侧较少的点的数量排序,按少到多的顺序加入多边形,显然每加入一条边都会形成一个不可分割的多边形,也就是一个节点。维护另外一侧仍要被继续分割的多边形的节点顺序即可。
  • 关于对多边形进行标号,我们只需要对每个多边形用vector维护一个降序的顶点集合,利用vector的字典序比较即可对多边形进行排序,从而标号。注意两个多边形至多会有两个公共点,因此字典序比较的复杂度是 O ( 1 ) O(1) O(1)的。

【代码】

#include
using namespace std;
const int MAXN = 100005;
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 edge {int x, y, val; } e[MAXN];
int n, m, tot, num[MAXN], ans[MAXN];
int root, weight[MAXN], size[MAXN];
int nxt[MAXN], home[MAXN];
bool vis[MAXN];
vector <int> a[MAXN], nodes[MAXN];
map <int, int> behind[MAXN];
bool cmp(edge x, edge y) {
	return x.val < y.val;
}
bool cnp(int x, int y) {
	return nodes[x] < nodes[y];
}
void getroot(int pos, int fa, int tot) {
	size[pos] = 1; weight[pos] = 0;
	for (unsigned i = 0; i < a[pos].size(); i++)
		if (a[pos][i] != fa && !vis[a[pos][i]]) {
			getroot(a[pos][i], pos, tot);
			size[pos] += size[a[pos][i]];
			chkmax(weight[pos], size[a[pos][i]]);
		}
	chkmax(weight[pos], tot - size[pos]);
	if (weight[pos] < weight[root]) root = pos;
}
void calcsize(int pos, int fa) {
	size[pos] = 1;
	for (unsigned i = 0; i < a[pos].size(); i++)
		if (a[pos][i] != fa && !vis[a[pos][i]]) {
			calcsize(a[pos][i], pos);
			size[pos] += size[a[pos][i]];
		}
}
void work(int pos, int depth) {
	vis[pos] = true;
	ans[num[pos]] = depth;
	calcsize(pos, 0);
	for (unsigned i = 0; i < a[pos].size(); i++)
		if (!vis[a[pos][i]]) {
			root = 0;
			getroot(a[pos][i], pos, size[a[pos][i]]);
			work(root, depth + 1);
		}
}
void debug(int pos) {
	printf("%d:\n", pos);
	printf("  Vertex:");
	for (unsigned i = 0; i < nodes[pos].size(); i++)
		printf(" %d", nodes[pos][i]);
	printf("\n");
	printf("  Edge:");
	for (unsigned i = 0; i < a[pos].size(); i++)
		printf(" %d", a[pos][i]);
	printf("\n");
	printf(" Number: %d\n", num[pos]);
}
int main() {
	read(n), read(m);
	for (int i = 1; i <= m; i++) {
		read(e[i].x), read(e[i].y);
		if (e[i].x > e[i].y) swap(e[i].x, e[i].y);
		e[i].val = e[i].y - e[i].x - 1;
		chkmin(e[i].val, n - 2 - e[i].val);
	}
	sort(e + 1, e + m + 1, cmp);
	for (int i = 1; i <= n; i++)
		if (i == n) nxt[n] = 1;
		else nxt[i] = i + 1;
	for (int i = 1; i <= m; i++) {
		int now = ++tot, s = e[i].y, t = e[i].x;
		if (e[i].y - e[i].x - 1 == e[i].val) swap(s, t);
		nodes[now].push_back(s);
		nodes[now].push_back(t);
		int p = s;
		while (nxt[s] != t) {
			int q = nxt[s];
			nodes[now].push_back(q);
			if (behind[p][q] != 0) {
				int tmp = behind[p][q];
				a[tmp].push_back(now);
				a[now].push_back(tmp);
			}
			nxt[s] = nxt[p = q];
		}
		if (behind[p][t] != 0) {
			int tmp = behind[p][t];
			a[tmp].push_back(now);
			a[now].push_back(tmp);
		}
		behind[s][t] = now;
		if (i != m) continue;
		now = ++tot; swap(s, t);
		a[now].push_back(now - 1);
		a[now - 1].push_back(now);
		nodes[now].push_back(s);
		nodes[now].push_back(t);
		p = s;
		while (nxt[s] != t) {
			int q = nxt[s];
			nodes[now].push_back(q);
			if (behind[p][q] != 0) {
				int tmp = behind[p][q];
				a[tmp].push_back(now);
				a[now].push_back(tmp);
			}
			nxt[s] = nxt[p = q];
		}
		if (behind[p][t] != 0) {
			int tmp = behind[p][t];
			a[tmp].push_back(now);
			a[now].push_back(tmp);
		}
		behind[s][t] = now;
	}
	if (m == 0) tot++;
	for (int i = 1; i <= tot; i++) {
		sort(nodes[i].begin(), nodes[i].end());
		reverse(nodes[i].begin(), nodes[i].end());
		home[i] = i;
	}
	sort(home + 1, home + tot + 1, cnp);
	for (int i = 1; i <= tot; i++)
		num[home[i]] = i;
	weight[root = 0] = n + 1;
	getroot(1, 0, n);
	work(root, 1);
	for (int i = 1; i <= tot; i++)
		printf("%d ", ans[i]);
	return 0;
}

**【G】**Sherlock and the Encrypted Data

【思路要点】

  • 我们发现数值是否减小仅和原数中出现的最大的十六进制数位有关。
  • 数位DP,记 f i , j , s f_{i,j,s} fi,j,s表示当前完成了最高的 i i i位,已经不再紧贴上界,当前出现的最大十六进制数位为 j j j,最后16位已经出现的数值为 s s s
  • f i , j , s f_{i,j,s} fi,j,s可以在 O ( 2 16 ∗ 1 6 3 ) O(2^{16}*16^3) O(216163)的时间内通过简单数位DP求出。
  • 对于每个询问,我们只需要额外对于紧贴上界部分进行计算即可。
  • 时间复杂度 O ( 2 16 ∗ 1 6 3 + Q ∗ 1 6 2 ) O(2^{16}*16^3+Q*16^2) O(216163+Q162)

【代码】

#include
using namespace std;
const int MAXN = 17;
const int MAXS = 1 << 16;
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 a[MAXN]; char s[MAXN];
long long dp[MAXN][MAXN][MAXS];
long long work(bool flg, int pos, int Max, int s) {
	if (pos == 0) {
		return ((1 << Max) & s) != 0;
	}
	if (flg) {
		long long ans = 0;
		for (int i = 0; i <= a[pos]; i++)
			if (pos <= 4) ans += work(i == a[pos], pos - 1, max(Max, i), s ^ (i << (pos - 1) * 4));
			else ans += work(i == a[pos], pos - 1, max(Max, i), s);
		return ans;
	} else {
		if (dp[pos][Max][s] != -1) return dp[pos][Max][s];
		long long ans = 0;
		for (int i = 0; i <= 15; i++)
			if (pos <= 4) ans += work(false, pos - 1, max(Max, i), s ^ (i << (pos - 1) * 4));
			else ans += work(false, pos - 1, max(Max, i), s);
		return dp[pos][Max][s] = ans;
	}
}
long long solve() {
	return work(true, 16, 0, 0);
}
int main() {
	int T; read(T);
	memset(dp, -1, sizeof(dp));
	while (T--) {
		scanf("%s", s + 1);
		int len = strlen(s + 1);
		memset(a, 0, sizeof(a));
		for (int i = 1; i <= len; i++) {
			int now = s[i] - '0';
			if (s[i] >= 'a') now = s[i] - 'a' + 10;
			a[len - i + 1] = now;
		}
		if (len == 1 && a[1] == 0) a[1]++;
		for (int i = 1; true; i++)
			if (a[i]) {
				a[i]--;
				break;
			} else a[i] = 15;
		long long ans = -solve();
		scanf("%s", s + 1);
		len = strlen(s + 1);
		memset(a, 0, sizeof(a));
		for (int i = 1; i <= len; i++) {
			int now = s[i] - '0';
			if (s[i] >= 'a') now = s[i] - 'a' + 10;
			a[len - i + 1] = now;
		}
		ans += solve();
		writeln(ans);
	}
	return 0;
}

你可能感兴趣的:(【OJ】CodeForces,【类型】做题记录,【比赛】CodeForces,【算法】数学,【算法】差分与前缀和思想,【算法】2-SAT,【数据结构】并查集,【算法】欧拉函数,【算法】平面图转对偶图,【算法】平面图相关,【算法】点分治,【算法】数位DP,【算法】动态规划)