A:
题目:
现在存在一序列,每个序列都有自己的颜色,颜色的花费为,该颜色所有的元素最大值-最小值。
现在你可以任意着色,问你着色的最大花费。
思路:
毫无疑问颜色越多越好,那么两两一组,保证最大值 - 最小值最大,那么排序后,两边的差值最大。
做法:
所有元素排序,双指针,头尾的差值依次添加,剩余的不要
代码
void solved()
{
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
sort(a +1, a +1 + n);
int ans = 0;
int r = n, l = 1;
while(l < r)
{
ans += a[r] - a[l];
r--, l++;
}
cout << ans << endl;
}
B:
题目:
现在存在一个数组,你可以选择一段区间,使得区间内的数字都乘 - 1,问你在保证最大所有元素和的情况下,最小操作次数
思路:
毫无疑问,保证最大元素和,就要把所有的负数都变成正数,保证最小操作次数,尽可能的保证负数都在一个区间内。
做法:
从前往后,记录标记是否遇到负数,如果遇到负数如果标记 false,那么标记上,为 true ,那么目前是连续的负数。
如果是正数,改变标记即可。并且变为 true的次数即可。
代码
void solved()
{
cin >> n;
LL sum = 0;
for(int i = 1; i <= n; i++) cin >> a[i], sum += abs(a[i]);
LL ans = 0;
bool flag = false;
for(int i = 1; i <= n; i++)
{
if(a[i] == 0) continue;
if(a[i] < 0)
{
if(!flag) ans++, flag = true;
}else
{
if(flag) flag = false;
}
}
cout << sum << " " << ans << endl;
}
C:
题目:
现在存在有一个二叉树,每次按层来编号,现在给出一个 n, 问你 1 - n 路径上所有编号的和是多少。
思路:
不难发现给出 n,我们依次找到父亲加起来即可,父亲为 子节点的编号 / 2,依次向上即可。
做法:
每次 / 2, 依次向上找父亲,直到找到1。
代码
void dfs(int u, int fa)
{
bool flag = false;
for(int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
if(j == fa) continue;
flag = true;
dfs(j, u);
kid[u] += kid[j];
}
if(!flag) kid[u]++;
}
void solved()
{
idx = 0;
memset(h, -1, sizeof h);
for(int i = 1; i <= n; i++) kid[i] = 0;
cin >> n;
for(int i = 1; i < n; i++)
{
int x, y; cin >> x >> y;
add(x, y), add(y, x);
}
dfs(1, -1);
cin >> m;
int ans = 0;
for(int i = 0; i < m; i++)
{
int x, y; cin >> x >> y;
cout << kid[x] * kid[y] <<endl;
}
}
D:
题目:
现在存在一颗树,如果说存在一个点有苹果,那么一次操作后,苹果随机到它的孩子上,如果没孩子,苹果掉落。
现在存在一对数字 [HTML_REMOVED], 在有限次操作后, a 和 b 点上的苹果都掉落从 [HTML_REMOVED],点掉落,问你点对的个数。
思路:
只需要知道两个点下面的叶子节点有多少个,然后计算即可。
做法:
dfs函数记录每个点的叶子节点,然后乘法原理计算点对的个数
代码
void solved()
{
idx = 0;
memset(h, -1, sizeof h);
for(int i = 1; i <= n; i++) kid[i] = 0, du[i] = 0;
cin >> n;
for(int i = 1; i < n; i++)
{
int x, y; cin >> x >> y;
add(x, y), add(y, x);
du[x]++, du[y]++;
}
dfs(1, -1);
cin >> m;
int ans = 0;
for(int i = 0; i < m; i++)
{
int x, y; cin >> x >> y;
cout << kid[x] * kid[y] <<endl;
}
}
E:
题目:
现在存在一个 n 个 0 构成 的数组,我们定义一个区间内 1 的 个数 大于 0 的个数那么 这个区间是美丽区间。
现在预先给出一些区间,和一些操作,每个操作会给出 x,将 x 位置上变为 1。
现在找到下标最小并且第一次变成美丽区间的下标
思路:
观察数据范围都是 n,每次添加 x位置后,前缀和查询每个区间的是否美丽复杂度是 n ,因为要添加 q 次,
复杂度是 n * n 是不可行的。不难发现,他让我们找到第一次存在美丽区间的下标,那么我们通过二分可以优化掉一个n。
二分前 mid 次操作,是否存在美丽区间,如果前面存在那么后面一定存在。因为复杂度为 nlogn
做法:
二分出来mid 次, check函数中,前缀和处理 前 mid 次区间 1 的个数,然后再查询存不存在美丽区间。
最终二分出来第一次存在美丽区间的下标。
代码
bool check(int mid)
{
for(int i = 1; i <= n; i++) qz[i] = 0;
for(int i = 1; i <= mid; i++)
{
qz[point[i]]++;
}
for(int i = 1; i <= n; i++) qz[i] += qz[i - 1];
for(int i = 1; i <= m; i++)
{
int len = qj[i].second - qj[i].first + 1;
int cnt = qz[qj[i].second] - qz[qj[i].first - 1];
if(cnt > len - cnt) return true;
}
return false;
}
void solved()
{
cin >> n >> m;
for(int i = 0; i <= n + 1; i++) qz[i] = 0, qj[i].first = 0, qj[i].second = 0, point[i] = 0;
for(int i = 1; i <= m; i++) cin >> qj[i].first >> qj[i].second;
cin >>q;
for(int i = 1; i <= q; i++) cin >> point[i];
if(!check(n))
{
cout << -1 << endl;
return;
}
int l = 1, r = q;
while(l < r)
{
int mid = l + r >> 1;
if(check(mid)) r = mid;
else l = mid + 1;
}
cout << r << endl;
}