【CodeForces】Codeforces Round 614
作者:互联网
比赛链接
官方题解
Problem A. NEKO’s Maze Game
注意到保证了 (1,1) 和 (2,n) 始终不会出现障碍,
能够从 (1,1) 到达 (2,n) 当且仅当没有出现路径被隔断的情况。
那么,维护隔断路径的点对的数量 Ans ,每次修改后 O(1) 计算 Ans 的增量即可。
时间复杂度 O(Q) 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
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;
}
bool a[2][MAXN];
int main() {
int n, m; read(n), read(m);
int ans = 0;
for (int i = 1; i <= m; i++) {
int x, y; read(x), read(y), x--;
if (a[x][y]) {
a[x][y] = false;
for (int j = -1; j <= 1; j++)
ans -= a[x ^ 1][y + j];
} else {
a[x][y] = true;
for (int j = -1; j <= 1; j++)
ans += a[x ^ 1][y + j];
}
if (ans) puts("No");
else puts("Yes");
}
return 0;
}
Problem B. Aroma’s Search
注意到给定的坐标序列两维坐标是分别单调的,可以认为我们能够访问的坐标编号是一个区间。
枚举这个区间 [l,r] ,以及 l,r 中较早被访问的点,判断是否可行即可。
时间复杂度 O(Log2T) 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
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;
}
ll x[MAXN], y[MAXN], ax, ay, bx, by, sx, sy, t;
ll absll(ll x) {
if (x < 0) return -x;
else return x;
}
int main() {
int n = 1; read(x[1]), read(y[1]);
read(ax), read(ay), read(bx), read(by);
read(sx), read(sy), read(t);
while (x[n] <= sx + t && y[n] <= sy + t) {
n++;
x[n] = ax * x[n - 1] + bx;
y[n] = ay * y[n - 1] + by;
}
int ans = 0;
for (int i = 1; i <= n; i++)
for (int j = i; j <= n; j++) {
ll dis = absll(x[i] - x[j]) + absll(y[i] - y[j]);
ll lft = t - dis;
if (lft >= 0) {
if (absll(sx - x[i]) + absll(sy - y[i]) <= lft) chkmax(ans, j - i + 1);
if (absll(sx - x[j]) + absll(sy - y[j]) <= lft) chkmax(ans, j - i + 1);
}
}
cout << ans << endl;
return 0;
}
Problem C. Xenon’s Attack on the Gangs
考虑从小到大填入权值,则每次填入的权值要想对 S 有贡献,就必须与之前填入的所有权值在同一条路径 P 上,此时产生的贡献为包含 P 的路径数。
那么,显然每次在 P 的两段填入新的权值是更优的。
枚举最后一次产生贡献的填入后的 P ,简单 DP 即可。
时间复杂度 O(N2) 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3005;
typedef long long ll;
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;
}
int n, size[MAXN][MAXN], nxt[MAXN][MAXN], len[MAXN][MAXN];
int x[MAXN], y[MAXN]; ll dp[MAXN][MAXN];
vector <int> a[MAXN];
void dfs(int root, int fa, int pos) {
size[root][pos] = 1;
len[root][pos] = len[root][fa] + 1;
if (fa == root) nxt[root][pos] = pos;
else nxt[root][pos] = nxt[root][fa];
for (auto x : a[pos])
if (x != fa) {
dfs(root, pos, x);
size[root][pos] += size[root][x];
}
}
ll getans(int x, int y) {
if (x == y) return 0;
if (dp[x][y] != -1) return dp[x][y];
return dp[x][y] = (n - size[x][nxt[x][y]]) * (n - size[y][nxt[y][x]]) + max(getans(nxt[x][y], y), getans(x, nxt[y][x]));
}
int main() {
read(n);
for (int i = 1; i <= n - 1; i++) {
read(x[i]), read(y[i]);
a[x[i]].push_back(y[i]);
a[y[i]].push_back(x[i]);
}
for (int i = 1; i <= n; i++) {
size[i][i] = n;
for (auto x : a[i])
dfs(i, i, x);
}
memset(dp, -1, sizeof(dp));
ll ans = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
chkmax(ans, getans(i, j));
cout << ans << endl;
return 0;
}
Problem D. Chaotic V.
可能涉及到的点只有 K=5000 个,若能建立它们的虚树,则可以转化成一个图论问题。
为了建立虚树,我们需要能够计算两数的 Lca ,以及比较两数的深度。筛出 K 以内的质数,用因子分解的形式表示一个数,不难达成以上要求。
建立完虚树后,将关键点选在树的一个权值重心即可。
时间复杂度 O(LogKK2+N) 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
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;
}
int tot, prime[MAXN], f[MAXN], home[MAXN];
void sieve(int n) {
for (int i = 2; i <= n; i++) {
if (f[i] == 0) {
prime[++tot] = f[i] = i;
home[i] = tot;
}
for (int j = 1; j <= tot && prime[j] <= f[i]; j++) {
int tmp = prime[j] * i;
if (tmp > n) break;
f[tmp] = prime[j];
}
}
}
int m; vector <int> num[MAXN];
int dist(vector <int> a, vector <int> b) {
int ans = 0;
for (int i = 1; i <= tot; i++)
ans += abs(a[i] - b[i]);
return ans;
}
vector <int> lca(vector <int> a, vector <int> b) {
for (int i = tot; i >= 1; i--) {
if (a[i] != b[i]) {
chkmin(a[i], b[i]);
for (int j = i - 1; j >= 1; j--)
a[j] = 0;
return a;
}
}
return a;
}
vector <pair <int, int>> a[MAXN];
int q, root, cnt[MAXN], sum[MAXN]; ll ans;
void addedge(int x, int y) {
int d = dist(num[x], num[y]);
a[x].emplace_back(y, d);
a[y].emplace_back(x, d);
}
void dfs(int pos, int fa) {
sum[pos] = cnt[pos];
for (auto x : a[pos])
if (x.first != fa) {
dfs(x.first, pos);
sum[pos] += sum[x.first];
}
}
void getroot(int pos, int fa) {
root = pos;
for (auto x : a[pos])
if (x.first != fa && sum[x.first] * 2 > q) {
getroot(x.first, pos);
return;
}
}
void getans(int pos, int fa, int len) {
ans += 1ll * len * cnt[pos];
for (auto x : a[pos])
if (x.first != fa) getans(x.first, pos, len + x.second);
}
int main() {
int n = 5000; m = n, sieve(n);
num[0].resize(tot + 1);
for (int i = 1; i <= n; i++) {
num[i] = num[i - 1];
int tmp = i;
while (tmp != 1) {
num[i][home[f[tmp]]]++;
tmp /= f[tmp];
}
}
static int Stack[MAXN];
int top = 1; Stack[top = 1] = 1;
for (int i = 2; i <= n; i++) {
vector <int> tmp = lca(num[i], num[Stack[top]]);
if (tmp == num[Stack[top]]) Stack[++top] = i;
else {
while (tmp < num[Stack[top - 1]]) {
addedge(Stack[top], Stack[top - 1]);
top--;
}
if (tmp == num[Stack[top - 1]]) {
addedge(Stack[top], Stack[top - 1]);
Stack[top] = i;
} else {
num[++m] = tmp;
addedge(Stack[top], m);
Stack[top] = m;
Stack[++top] = i;
}
}
}
for (int i = 1; i <= top - 1; i++)
addedge(Stack[i], Stack[i + 1]);
read(q);
for (int i = 1; i <= q; i++) {
int x; read(x);
chkmax(x, 1);
cnt[x]++;
}
dfs(1, 0);
getroot(1, 0);
getans(root, 0, 0);
cout << ans << endl;
return 0;
}
Problem E. Rin and The Unknown Flower
考虑首先询问 CO,CH,HO,HC ,此时的花费为 1 。
此后,对于没有确定的位置,有如下可能:
1 、连续两个以 O 开头的字符
2 、连续两个相同的字符
分如下两种情况讨论。
(1) 、没有确定任何一个字符
此时,字符串应当有连续的若干个 O 开头,并且剩余的位置均为 C,H 中的一种。
询问 CCC,HHH ,若确定了出现位置,则剩下没有被确定的位置均为 O ;
否则,询问 OOO ,若确定了出现位置,则再花费一次询问判断剩余的位置上是 C 还是 H ;
否则,原串一定是 OOCC,OOHH 中的一种,花费一次询问判断即可。
当 N=4 时有最大花费 1+93+161<1.4 。
(2) 、确定了某些字符
找到最靠右的确定的位置 x ,令 [l,r]=[x,x] 。
若 l−1 上的字符已被确定,则令 l=l−1 ;
否则,若 sl=C , l−1 上的字符应为 C,O 中的一种, 花费一次询问判断;
否则,若 sl=H , l−1 上的字符应为 H,O 中的一种, 花费一次询问判断;
否则,即 sl=O , l−1 上的字符一定是 O 。
此时,我们确定了原串的一个前缀 [1,r] 。
若 sr=C ,则 r−1 上的字符一定是 C ;
否则,若 sr=H ,则 r−1 上的字符一定是 H ;
否则,即 sr=O ,首先花费一次询问判断 r−1 上的字符是否是 O ,若不是,则可以再花费一次询问确定整个字符串。
最大花费为
maxi∈[4,50]{1+j=3∑ij21+i21}<1.4
单组数据时间复杂度 O(N2) 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
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;
}
char s[MAXN]; int n, k;
void answer(int n) {
cout << '!' << ' ';
for (int i = 1; i <= n; i++)
cout << s[i];
cout << endl;
}
bool get(int v) {
read(k); if (k == -1) exit(0);
bool ans = false;
while (k--) {
int x; read(x);
if (v == x) ans = true;
}
return ans;
}
int main() {
int T; read(T);
while (T--) {
read(n);
for (int i = 1; i <= n; i++)
s[i] = 0;
cout << '?' << ' ' << "CH" << endl;
read(k); if (k == -1) exit(0);
while (k--) {
int x; read(x);
s[x] = 'C', s[x + 1] = 'H';
}
cout << '?' << ' ' << "CO" << endl;
read(k); if (k == -1) exit(0);
while (k--) {
int x; read(x);
s[x] = 'C', s[x + 1] = 'O';
}
cout << '?' << ' ' << "HO" << endl;
read(k); if (k == -1) exit(0);
while (k--) {
int x; read(x);
s[x] = 'H', s[x + 1] = 'O';
}
cout << '?' << ' ' << "HC" << endl;
read(k); if (k == -1) exit(0);
while (k--) {
int x; read(x);
s[x] = 'H', s[x + 1] = 'C';
}
int l = 0, r = 0;
for (int i = 1; i <= n; i++)
if (s[i]) l = r = i;
if (l == 0) {
cout << '?' << ' ' << "CCC" << endl;
read(k); if (k == -1) exit(0);
while (k--) {
int x; read(x);
s[x] = s[x + 1] = s[x + 2] = 'C';
}
cout << '?' << ' ' << "HHH" << endl;
read(k); if (k == -1) exit(0);
while (k--) {
int x; read(x);
s[x] = s[x + 1] = s[x + 2] = 'H';
}
if (s[n]) {
for (int i = 1; i <= n; i++)
if (s[i] == 0) s[i] = 'O';
} else {
cout << '?' << ' ' << "OOO" << endl;
read(k); if (k == -1) exit(0);
while (k--) {
int x; read(x);
s[x] = s[x + 1] = s[x + 2] = 'O';
}
if (s[1]) {
for (int i = 1; i <= n; i++)
if (s[i] == 0) s[i] = 'C';
cout << '?' << ' ';
for (int i = 1; i <= n; i++)
cout << s[i];
cout << endl;
if (!get(1)) {
for (int i = 1; i <= n; i++)
if (s[i] == 'C') s[i] = 'H';
}
} else {
assert(n == 4);
cout << "? OOCC" << endl;
if (get(1)) s[1] = s[2] = 'O', s[3] = s[4] = 'C';
else s[1] = s[2] = 'O', s[3] = s[4] = 'H';
}
}
} else {
while (l != 1 || r != n) {
if (l > 1 && s[l - 1]) l--;
else if (l > 1) {
if (s[l] == 'C') {
cout << '?' << ' ' << 'O';
for (int i = l; i <= r; i++)
cout << s[i];
cout << endl;
if (get(l - 1)) s[l - 1] = 'O';
else s[l - 1] = 'C';
} else if (s[l] == 'H') {
cout << '?' << ' ' << 'O';
for (int i = l; i <= r; i++)
cout << s[i];
cout << endl;
if (get(l - 1)) s[l - 1] = 'O';
else s[l - 1] = 'H';
} else s[l - 1] = 'O';
l--;
} else {
if (s[r] == 'O') {
cout << '?' << ' ';
for (int i = l; i <= r; i++)
cout << s[i];
cout << 'O' << endl;
if (get(l)) s[r + 1] = 'O';
else {
cout << '?' << ' ';
for (int i = l; i <= r; i++)
cout << s[i];
cout << 'C' << endl;
if (get(l)) s[r + 1] = 'C';
else s[r + 1] = 'H';
}
} else s[r + 1] = s[r];
r++;
}
}
}
answer(n);
int res; read(res);
if (!res) exit(0);
}
return 0;
}
Problem F. Nora’s Toy Boxes
定义本源的数字是集合中不存在非平凡因数,且存在非平凡倍数的数字。
对于操作 (i,j,k) ,我们不妨认为 ai 必须是本源的数字。
又因为本源的数字无法被删除,我们可以认为与一次操作相关的只有 aj,ak 两个数,对于可以通过一次操作删除其一的两个数 aj,ak ,在它们之间连一条边,则显然,各个联通块的答案是独立的,可以用隔板法合并。
考虑如何对删除序列计数,对于一个连通块,显然最终至少会留下一个数。
将删除的过程倒转,考虑由留下的这个数向外拓展的过程,则在状态中计入当前的点数 cnt ,以及这些点所覆盖的本源的数字集合 mask ,即可转移。
时间复杂度 O(2M×N2) ,其中 M 表示本源的数字个数,有 M≤12 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 65;
const int MAXS = 4096;
const int P = 1e9 + 7;
typedef long long ll;
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;
}
int n, m, bit[MAXN], a[MAXN], key[MAXN];
int f[MAXN], binom[MAXN][MAXN], dp[MAXS][MAXN];
int find(int x) {
if (f[x] == x) return x;
else return f[x] = find(f[x]);
}
int getcnt(int x) {
int ans = -1;
for (int i = 1; i <= n; i++)
if (f[i] == x) ans += key[i] == -1;
return max(ans, 0);
}
void update(int &x, int y) {
x += y;
if (x >= P) x -= P;
}
int getans(int x) {
int cnt = getcnt(x), m = 0;
if (cnt == 0) return 1; cnt++;
static int ins[MAXS], type[MAXN];
for (int i = 1; i <= n; i++)
if (f[i] == x && key[i] != -1) key[i] = ++m;
for (int i = 0; i < (1 << m); i++)
ins[i] = 0;
for (int i = 1; i <= n; i++)
if (f[i] == x && key[i] == -1) {
type[i] = 0;
for (int j = 1; j <= n; j++)
if (f[j] == x && key[j] != -1 && a[i] % a[j] == 0) type[i] |= 1 << (key[j] - 1);
for (int j = 0; j < (1 << m); j++)
ins[j] += (type[i] | j) == j;
}
static int dp[MAXN][MAXS];
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; i++)
if (f[i] == x && key[i] == -1) update(dp[1][type[i]], 1);
for (int i = 1; i <= cnt - 1; i++)
for (int j = 0; j < (1 << m); j++) {
int tmp = dp[i][j];
if (!tmp) continue;
update(dp[i + 1][j], 1ll * tmp * (ins[j] - i) % P);
for (int k = 1; k <= n; k++)
if (f[k] == x && key[k] == -1 && (type[k] & j) != 0 && (type[k] | j) != j) update(dp[i + 1][j | type[k]], tmp);
}
return dp[cnt][(1 << m) - 1];
}
int main() {
read(n);
for (int i = 1; i <= n; i++)
read(a[i]), f[i] = i;
for (int i = 0; i <= n; i++) {
binom[i][0] = 1;
for (int j = 1; j <= i; j++)
binom[i][j] = (binom[i - 1][j - 1] + binom[i - 1][j]) % P;
}
sort(a + 1, a + n + 1);
for (int i = 1; i <= n; i++) {
if (key[i] == -1) continue;
bool vis = false;
for (int j = i + 1; j <= n; j++)
if (a[j] % a[i] == 0) {
vis = true;
key[j] = -1;
f[find(i)] = find(j);
}
if (vis) key[i] = ++m;
else key[i] = -1;
}
for (int i = 1; i <= n; i++)
f[i] = find(i);
assert(m <= 12);
int ans = 1, cnt = 0;
for (int i = 1; i <= n; i++)
if (f[i] == i) {
ans = 1ll * ans * getans(i) % P * binom[cnt + getcnt(i)][cnt] % P;
cnt += getcnt(i);
}
cout << ans << endl;
return 0;
}
cz_xuyixuan
发布了794 篇原创文章 · 获赞 82 · 访问量 16万+
关注
标签:int,void,CodeForces,614,pos,MAXN,template,Round,getchar 来源: https://blog.csdn.net/qq_39972971/article/details/104052408