CF Round 775 Div2 题解
A题 Game
给定一个长度为 \(n\) 的一维线性地图,位置 \(i\) 处可能是陆地或者水。
现在我们要从位置 \(1\) 到达位置 \(n\) 处(保证这两个地方都是水),我们有两种方式:
- 如果位置 \(i\) 和位置 \(i+1\) 都是陆地,那么可以不耗费代价来互相移动
- 可以从位置 \(x\) 跳到位置 \(y\),消耗代价为 \(|y-x|\) (该操作至多进行一次)
问需要花费至少多少代价来从位置 \(1\) 到达位置 \(n\)。
\(1\leq n \leq 100\)
从头尾分别延伸最长的连续陆地串,然后中间直接跳即可。
#include<bits/stdc++.h>
using namespace std;
const int N = 110;
int n, a[N];
int solve() {
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> a[i];
//
bool flag = true;
for (int i = 1; i <= n; ++i)
if (a[i] == 0) flag = false;
if (flag) return 0;
int l, r;
for (int i = 1; i <= n; ++i)
if (a[i] == 0) {
l = i - 1;
break;
}
for (int i = n; i >= 1; i--)
if (a[i] == 0) {
r = i + 1;
break;
}
return r - l;
}
int main()
{
int T;
cin >> T;
while (T--) cout << solve() << endl;
return 0;
}
B题 Game of Ball Passing (数学)
建议直接看原题面
我们对整个数组从小到大排个序,记整个数组前 \(n-1\) 个元素之和为 \(L\),最大元素的值为 \(R\)。
当 \(L+1\geq R\) 的时候,不难构造出只含有一个球的方案。
当 \(L+1<R\) 时,我们发现,每次一个球都会使得 \(R\) 和 \(L\) 的差值减小 1,所以答案为 \(R-L\) 。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 100010;
int n;
LL a[N];
int solve() {
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> a[i];
sort(a + 1, a + n + 1);
if (a[n] == 0) return 0;
LL L = 0, R = a[n];
for (int i = 1; i < n; ++i) L += a[i];
return max(R - L, 1LL);
}
int main()
{
int T;
cin >> T;
while (T--) cout << solve() << endl;
return 0;
}
C题 Weird Sum(前缀和)
给定一张 \(n\) 行 \(m\) 列的地图,每个格子都有一个自己的颜色 \(c_{i,j}\)。
我们记同一颜色 \(x\) 的格子的集合为 \(S_x\),那么 \(f(S_x)\) 为集合内格子两两曼哈顿距离之和,试求出 \(\sum\limits_{x\in M} f(S_x)\) 的值。(\(M\) 为地图中所有颜色之和)
\(1\leq n \leq m,nm\leq 10^6,1\leq c_{i,j}\leq 10^6\)
这个显然,我们开 \(10^6\) 个 vector,讲相同颜色的格子放入对应颜色的 vector 里面,然后直接对每个 vector 求解即可。
不过,我们平方枚举肯定不行,得优化到线性复杂度。
我们考虑坐标仅有一维的情况,那么我们直接将坐标排个序,然后做一次前缀和,按照算贡献的方式来统计答案,就能够将平方枚举优化到线性。
对于二维的曼哈顿距离,由于两轴坐标互相独立,互不影响,所以我们直接分别排序统计后相加即可。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int C = 100010;
int n, m;
vector<pair<int, int> > color[C];
LL x[C], y[C], xx[C], yy[C];
LL solve(int c) {
vector<pair<int, int> > &arr = color[c];
int cnt = 0;
for (auto it : arr) x[++cnt] = it.first, y[cnt] = it.second;
sort(x + 1, x + cnt + 1);
sort(y + 1, y + cnt + 1);
for (int i = 1; i <= cnt; ++i)
xx[i] = xx[i - 1] + x[i], yy[i] = yy[i - 1] + y[i];
LL res = 0;
for (int i = 1; i < cnt; ++i) {
int len = cnt - i;
res += (xx[cnt] - xx[i]) - x[i] * len;
res += (yy[cnt] - yy[i]) - y[i] * len;
}
return res;
}
int main()
{
//read
cin >> n >> m;
for (int i = 1; i <= n; ++i)
for (int j = 1, c; j <= m; ++j) {
cin >> c;
color[c].push_back(make_pair(i, j));
}
//solve
LL ans = 0;
for (int i = 1; i < C; ++i) ans += solve(i);
cout << ans << endl;
return 0;
}
D题 Integral Array(倍数枚举)
给定一个数组 \(\{a_n\}\),问是否对于内部任意两个元素 \(x,y(x\geq y)\),\(\lfloor\frac{x}{y}\rfloor\) 也在数组内?
\(1\leq n\leq 10^6,1\leq a_i\leq c \leq 10^6\)
一个偏暴力的 TLE 写法
我们将数组排个序并 unique 一下,然后从小到大依次将数放进去,每次加入一个新数字的时候,只枚举 \(\sqrt{a_i}\) 范围内的数,如果这个数存在,就看看整除的结果在不在数组内;如果不在,则看范围 \([L,R]\) 之内的数是否存在(\(L,R\) 满足 \(a_i\) 除以区间内的数得到的结果都等于这个数,可以 \(O(1)\) 的计算 \(L,R\))。考虑到单点修改和区间查询,所以我们维护一个 set 或者树状数组/线段树。
总复杂度 \(O(n\sqrt c\log c)\),对于 \(10^5\) 的数据还能挣扎一下,但是 \(10^6\) 属实有点为难了。
正解:倍数枚举
我们一开始就创建好 vis 数组并做一次前缀和(我们上面那个方法是动态插入的,所以有一个 \(O(\log n)\) 的复杂度,这里直接是一开始全部处理好,然后实现 \(O(1)\) 查询)。
我们枚举每一种值,倘若这个值存在,我们记为 \(x\),那么我们直接枚举 \(\lfloor\frac{y}{x}\rfloor\) 的值,记为 \(k\),那么有 \(kx\leq y <k(x+1)\)。我们看看区间 \([kx,kx+x-1]\) 内是否存在数,如果存在,那么我们就要查询一下 \(k\) 是否存在了。
这个的复杂度就是纯 \(O(n+c\sqrt{c})\),其实也就是少了一个 \(O(\log c)\) 的复杂度。
#include<bits/stdc++.h>
using namespace std;
const int N = 1000010;
int n, c, a[N];
//
int vis[N], pre[N];
inline bool query(int L, int R) { return pre[R] > pre[L - 1]; }
//
bool solve()
{
//read
scanf("%d%d", &n, &c);
for (int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
//init
memset(vis, 0, sizeof(int) * (c + 1));
for (int i = 1; i <= n; ++i)
vis[a[i]] = 1;
for (int i = 1; i <= c; ++i)
pre[i] = pre[i - 1] + vis[i];
//solve
for (int x = 1; x <= c; ++x) {
if (!vis[x]) continue;
for (int k = 1; k * x <= c; ++k)
if (query(k * x, min(c, k * x + x - 1)) && !vis[k])
return false;
}
return true;
}
int main()
{
int T;
scanf("%d", &T);
while (T--) puts(solve() ? "Yes" : "No");
return 0;
}