[竞赛地址] https://ac.nowcoder.com/acm/contest/82345
本来想参加vp的,有事耽误了。
老规矩,先上题面,后上题解。做题顺序按提交人数来(从易到难)。
A. How Many Permutation?
给定一个长度为n的数组和数字k,问有多少排列,满足
- 任意两个相邻元素的差值都不小于k。
输出满足上述条件的、排列的个数。
I. A Math Problem
给定一个数组,q个查询,每次输入x,求表达式
F. Sequence
给定一个序列a,可以执行两种操作。
- 删除序列中的元素。
- 在序列中的元素前插入0。
可以执行上述操作任意次。问最多能使多少位置满足a[i]=i。
B. Ternary Tree
给定一个满三叉树,深度为n,有q次操作,每次输入节点k,表示删除k及其子树。求每次操作后的三叉树剩余节点数量。
n<=25
C. Good Partition
给定一组数,将其分成2个子数组,使得对于每个子数组,满足以下:
- 任意取数组中两个元素,它们的和不构成完全平方数
问是否存在这种划分,输出YES或NO
A. How Many Permutation?(签到)
直接暴力统计,可以用dfs枚举排列。
这里图方便,用了 next_permutation
int n, k;
void solve() {
scanf("%d%d", &n, &k);
vector<int> a(n);
for (int i = 0; i < n; ++i) {
scanf("%d", &a[i]);
}
sort(a.begin(), a.end());
int ans = 0;
do {
bool ok = 1;
for (int i = 0; i < n - 1; ++i) {
if (abs(a[i] - a[i+1]) < k) {
ok = 0;
break;
}
}
ans += ok;
} while (next_permutation(a.begin(), a.end()));
printf("%d\n", ans);
}
I. A Math Problem(签到)
预处理平方和和单次方和,注意开long long
int n, q, x;
void solve() {
scanf("%d%d", &n, &q);
vector<int> a(n);
ll p2 = 0, p1 = 0;
for (int i = 0; i < n; ++i) {
scanf("%d", &x);
p2 += 1ll * x * x;
p1 += x;
}
while (q--) {
scanf("%d", &x);
printf("%lld\n", p2 + (-2ll) * p1 * x + 1ll * n * x * x);
}
}
F. Sequence(dp/最长上升子序列)
套了个壳,实际就是经典的最长上升子序列。
使用贪心+二分,维护每个长度为i的子序列,使其末尾元素最小化,
int n, q, x;
void solve() {
scanf("%d", &n);
vector<int> a(n + 1);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
}
vector<int> dp(n + 1);
int len = 1, pos;
dp[1] = a[1];
for (int i = 2; i <= n; ++i) {
if (a[i] > dp[len]) {
dp[++len] = a[i];
} else {
pos = lower_bound(dp.begin() + 1, dp.begin() + len + 1, a[i]) - dp.begin();
dp[pos] = a[i];
}
}
printf("%d\n", len);
}
B. Ternary Tree(二分/模拟)
维护每个节点的被删除数。具体实现看代码,有详细注释
数据卡常比较严格,用空间需要省点。。
const int maxn = 27;
#define ll long long
ll n, m, k;
// map<ll, ll> del;
// map<ll, bool> vis;
unordered_map<ll, ll> del; // del[k]表示节点k下的子树被删除的节点数
unordered_map<ll, bool> vis; // vis[k]标记k是否被删除
ll p[maxn]; // 3^i
// ll mn[maxn], mx[maxn]; // 每一层的 边界值
ll init() {
// mn[0] = mx[0] = 1;
// for (int i = 1; i < n; ++i) {
// mn[i] = mn[i-1] * 3 - 1;
// mx[i] = mx[i-1] * 3 + 1;
// }
p[0] = 1;
ll res = p[0];
for (int i = 1; i < n; ++i) {
p[i] = p[i-1] * 3;
// mn[i] = mn[i-1] * 3 - 1;
// mx[i] = mx[i-1] * 3 + 1;
res += p[i];
}
p[n] = p[n-1] * 3;
return res;
}
ll cal(ll d) { // 求出深度d的子节点个数
return (p[n-d+1] - 1) / 2;
}
ll get_deep(ll k) { // 求出k所在深度
// for (int i = 0; i < n; ++i) {
// if (mn[i] <= k && k <= mx[i]) {
// return i + 1;
// }
// }
// return n;
ll l = 1, r = n, pos = 0;
ll mid;
while (l <= r) {
mid = (l + r) / 2;
ll num = ((p[mid] - 1) / 2);
if (num < k) {
pos = max(pos, mid);
l = mid + 1;
} else {
r = mid - 1;
}
}
return pos + 1;
}
bool check(ll k) { // 判断k是否被删除
while (k) { // 整体思路就是找祖先节点是否存在被删除的
if (vis[k]) {
return true;
}
k = (k + 1) / 3;
}
return false;
}
void solve() {
scanf("%lld%lld", &n, &m);
del.clear();
vis.clear(); //
ll all = init();
ll ans = all; //
while (m--) {
scanf("%lld", &k);
if (check(k)) { //
printf("%lld\n", ans);
continue;
}
vis[k] = 1;
ll d = get_deep(k); // 求出k所在深度
ll num = cal(d); // 求出深度d的子节点个数
ll res = num - del[k]; // 本次新增 删除数量。
while (k) { // 更新祖先节点的 被删除数
del[k] += res;
k = (k + 1) / 3;
}
ans = all - del[1];
printf("%lld\n", ans);
}
}
C. Good Partition(图论/二分图/染色)
将能构成完全平方数的数对连边,预处理求出所有关联的边。
判断图是否可以用2种颜色进行染色。
const int maxn = 100001;
int n, mx, m, k;
bool ok;
void solve() {
scanf("%d", &n);
mx = 0;
vector<bool> vis(maxn*2); // 标记
vector<int> a(n + 1);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
vis[a[i]] = 1;
mx = max(mx, a[i]);
}
vector<vector<int>> ve(mx + 1); // 图
vector<int> color(mx + 1, -1); // 染色
m = sqrt(2 * mx) + 1;
for (int i = 1; i <= n; ++i) {
int &x = a[i];
for (int j = m; j >= 1; --j) {
if (j * j < x) {
break;
}
int y = j * j - x;
if (x != y && y <= mx && vis[y]) {
ve[x].push_back(y);
}
}
}
function<void(int, int, int)> dfs = [&](int u, int p, int c) {
if (!ok) {
return;
}
if (color[u] != -1) {
if (color[u] != c) {
ok = 0;
}
return;
}
color[u] = c;
for (auto v: ve[u]) {
if (v == p) {
continue;
}
dfs(v, u, 1 - c);
}
};
ok = 1;
for (int i = 1; i <= n && ok; ++i) {
int &x = a[i];
if (color[x] == -1) {
dfs(x, -1, 0);
}
}
printf("%s\n", ok ? "YES" : "NO");
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。