- Codeforces Round #661 (Div. 3)
- A. Remove Smallest
- B. Gifts Fixing
- C. Boats Competition
- D. Binary String To Subsequences
- E1. Weights Division (hard version)
Codeforces Round #661 (Div. 3)
A. Remove Smallest
题意: 给定t个测试样例,每个测试样例给定n个数字,每次操作选择两个差值绝对值小于等于1的数字,删除较小的那个。问是否能够通过多次删除操作,使得n个数字最后只剩下一个。1 <= t <= 100, 1 <= n <= 50, 1 <= a[i] <= 100
题解: 由于每次都能选择两个差值绝对值小于等于1的数字,因此最好要是能够删除到只剩下一个那么绝对最相近的两个数字的差值小于等于1.因此,只需要,先排完序后,只需要顺着扫描一遍,是否相邻两个数字的差值绝对值小于等于1即可。
代码:
#include
using namespace std;
int const N = 110;
int a[N];
int n, t;
int main() {
cin >> t;
while (t--) {
int flg = true;
cin >> n;
for (int i = 1, x; i <= n; ++i)
cin >> a[i];
sort(a + 1, a + 1 + n);
for (int i = 2; i <= n; ++i) {
if (a[i] - a[i - 1] > 1) flg = false;
}
if (n == 1) flg = true;
if (flg) cout << "YES\n";
else cout << "NO\n";
}
return 0;
}
B. Gifts Fixing
题意: n份物品,每份都有a,b两种值。现在每次操作可以选择将其中一份的a减一或者b减一或者同时减一。问至少几次操作可以使得所有物品的a值相同,且所有物品的b值相同。
题解: 把a[i]减到a[i]的最小值,把b[i]减到b[i]的最小值,贪心即可。
代码:
#include
using namespace std;
typedef long long LL;
int const N = 110, INF = 1e9 + 10;
LL a[N], b[N];
LL n, t;
int main() {
cin >> t;
while (t--) {
cin >> n;
LL aminv = INF, bminv = INF;
for (int i = 1; i <= n; ++i) scanf("%lld", &a[i]), aminv = min(aminv, a[i]);
for (int i = 1; i <= n; ++i) scanf("%lld", &b[i]), bminv = min(bminv, b[i]);
long long res = 0;
for (int i = 1; i <= n; ++i) {
res += max(a[i] - aminv, b[i] - bminv);
}
cout << res << endl;
}
return 0;
}
C. Boats Competition
题意: 给定t个测试样例,每个测试样例给定n个数字w[i],求把n个数字两两组合,每组的和值相同,最多能够组合成为多少组? 1 <= t <= 1000, 1 <= n <= 50, 1 <= wi <= n
题解: 本题由于规模比较小,直接枚举和值,然后枚举左端点位置,再二分枚举右端点位置即可。这样其实比较麻烦,可以枚举和值,然后枚举第一个数字即可,判断权值和s-a[i]是否存在,然后加上计数,完全不需要二分法。
代码:
懒得写了,送上二分代码
#include
using namespace std;
typedef long long LL;
int const N = 100;
int a[N], n, cnt[N], t; // cnt[1]:1的数目
int main() {
scanf("%d", &t);
while (t--) {
memset(cnt, 0, sizeof cnt);
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
cnt[a[i]]++;
}
sort(a + 1, a + 1 + n);
int m = unique(a + 1, a + 1 + n) - a - 1;
// cout << "m:" << m << endl;
int res = -1;
for (int s = 2; s <= 2 * n; ++s) {
// cout << "cur s:" << s << endl;
int tmp = 0;
for (int i = 1; i <= m; ++i) {
int l = i, r = m;
while (l < r) {
int mid = (l + r) >> 1;
if (a[i] + a[mid] >= s) r = mid;
else l = mid + 1;
}
if (a[l] + a[i] == s) {
// cout << "l: " << i << " " << "r: " << l << endl;
if (a[l] == a[i] && cnt[a[l]] == 1) continue;
if (a[l] == a[i]) tmp += cnt[a[l]] / 2;
else tmp += min(cnt[a[l]], cnt[a[i]]);
}
}
res = max(res, tmp);
}
printf("%d\n", res);
}
return 0;
}
D. Binary String To Subsequences
题意: 给定T个测试样例,每个测试样例给定一个字符串,长度为n,要求把字符串中的每个字符分成多个01子序列,求最少能够划分为多少个01子序列,打印出每个字符属于哪个子序列。\(\sum_{}n\) <= 2*10^5^
题解: 只需要维护两个队列,一个队列存放结尾为0的字符串,一个队列存放结尾为1的字符串,然后每次判断当前字符s[i]是0还是1,如果是1,那么看是否能够插入结尾为0的队列中;如果是0则反之。
代码:
#include
using namespace std;
int const N = 2e5 + 10;
int T, n, idx, res[N];
string s;
queue q1, q0;
int main(){
cin >> T;
while (T--) {
memset(res, 0, sizeof res);
idx = 0;
while (q1.size()) q1.pop();
while (q0.size()) q0.pop();
cin >> n;
cin >> s;
for (int i = 0; i < s.size(); ++i) {
if (s[i] == '0') {
if (q1.empty()) {
idx++;
q0.push(idx);
res[i] = idx;
}
else {
auto t = q1.front();
q1.pop();
q0.push(t);
res[i] = t;
}
}
else {
if (q0.empty()) {
idx++;
q1.push(idx);
res[i] = idx;
}
else {
auto t = q0.front();
q0.pop();
q1.push(t);
res[i] = t;
}
}
}
cout << idx << endl;
for (int i = 0; i < n; ++i) printf("%d ", res[i]);
cout << endl;
}
return 0;
}
E1. Weights Division (hard version)
题意: 给定T个测试样例,每个测试样例给定n个点和一个S,这n个点组成一颗根为1的树。规定每次操作能够把一条边的权值w变化为floor(w / 2)。问最少需要多少次操作能够把\(\sum_{}len(root, leave)\) <= S。leave为叶节点,root为根节点。1≤t≤210^4^, 2≤n≤10^5^;1≤S≤10^16^, 1≤vi,ui≤n;1≤wi≤10^6^,\(\sum_{}n\) <= 10^5^
题解: 求出每条跟到叶节点的距离,然后动态修改,这样很难处理。可以转化思路,求出每天边权值对于总答案的贡献,然后变化的适合只要改变每条边权值即可。每条边被经过的次数是固定的,只有权值是变化的。为了操作的次数最少,那么只需要按照每次操作能够减少最多的差值的按照从大到小排序即可。因此,维护一个优先队列,优先队列按照(t.w-t.w/2)t.cnt从大到小排序即可。
代码:
#include
using namespace std;
typedef long long LL;
int const N = 1e5 + 10;
int e[N * 2], w[N * 2], ne[N * 2], h[N], idx, n, m, T;
LL times[N * 2];
struct Edge {
LL ti, wi;
bool operator<(const Edge &e) const {
return (wi - wi / 2) * ti < (e.wi - e.wi / 2) * e.ti;
}
};
priority_queue, less > q;
LL sum, S;
void add(int a, int b, int c){
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int u, int fa) {
int wi = 0;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (j == fa) {
wi = w[i];
continue;
}
dfs(j, u);
times[u] += times[j];
}
if (!wi) return;
if (e[h[u]] == fa && ne[h[u]] == -1) times[u] = 1;
q.push({times[u], wi});
sum += times[u] * wi;
return;
}
int main(){
cin >> T;
while (T--) {
while (q.size()) q.pop();
sum = 0;
memset(h, -1, sizeof h);
memset(times, 0, sizeof times);
idx = 0;
scanf("%lld%lld", &n, &S);
for(int i = 1, a, b, c; i <= n - 1; ++i) {
scanf("%d%d%d", &a, &b, &c);
add(a, b, c), add(b, a, c);
}
dfs(1, -1);
LL res = 0;
while (sum > S) {
auto t = q.top();
q.pop();
sum -= t.ti * t.wi;
t.wi /= 2;
sum += t.ti * t.wi;
res ++;
q.push(t);
}
printf("%lld\n", res);
}
return 0;
}