【比赛链接】
【题解链接】
**【A】**A Serial Killer
【思路要点】
- 维护两个字符串模拟。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h>
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
【思路要点】
- 当 ,将所有数涂为一种颜色。
- 否则,将质数涂为一种颜色,非质数涂为一种颜色。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h>
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
【思路要点】
- 枚举可能的序列和(至多 种)。
- 枚举右端点,维护一个以前缀和为下标的左端点map,查询对应合法的左端点的个数即可。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h>
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的解法。
- 注意到本题中所建出的图为无向图,我们只需要用并查集简单维护即可。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h>
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
【思路要点】
- 不难发现 。
- 题目本质上是对 求了 次欧拉函数。
- 注意到奇数求欧拉函数后会变为偶数,而偶数求欧拉函数后会减半,所以 在求 次欧拉函数后就会变成1。
- 变成1后终止该过程即可。
- 注意输出时对 取模。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h>
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
【思路要点】
- 显然如果将题目中的多边形转成对偶图,那么会得到一棵树。
- 对这棵树进行树分治,每次在分治重心上标上其在点分树上的深度即是一组符合条件的解。
- 时间复杂度 。
- 关于实现上的一些细节:
- 我们当然可以使用传统的平面图转对偶图的方法来解题,但我们也可以利用本题的特殊性得到一个 编程复杂度更低的做法:对所有边按照两侧较少的点的数量排序,按少到多的顺序加入多边形,显然每加入一条边都会形成一个不可分割的多边形,也就是一个节点。维护另外一侧仍要被继续分割的多边形的节点顺序即可。
- 关于对多边形进行标号,我们只需要对每个多边形用vector维护一个降序的顶点集合,利用vector的字典序比较即可对多边形进行排序,从而标号。注意两个多边形至多会有两个公共点,因此字典序比较的复杂度是 的。
【代码】
#include<bits/stdc++.h>
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,记 表示当前完成了最高的 位,已经不再紧贴上界,当前出现的最大十六进制数位为 ,最后16位已经出现的数值为 。
- 可以在 的时间内通过简单数位DP求出。
- 对于每个询问,我们只需要额外对于紧贴上界部分进行计算即可。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h>
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;
}