A. Remove Smallest
大意:
给出n个数,每次可以选择两个差小于等于1的数,然后删掉其中的任意一个,问最后能不能只剩下一个元素
思路:
直接看有没有两个点的差大于2即可
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
int t, a[55];
int main() {
cin >> t;
while (t--) {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
sort(a, a + n);
int flag = 1;
for (int i = 1; i < n; i++) {
if (a[i] - a[i - 1] > 1) flag = 0;
}
if (flag)
cout << "YES" << endl;
else
cout << "NO" << endl;
}
return 0;
}
B. Gifts Fixing
大意:
给出两个长度为n的数组a和b,每次可以选择将(a_i)减一或者是(b_i)减一或者是(a_i)和(b_i)同时减一
问经过最少多少次操作可以使得所有的a都相同,所有的b都相同
思路:
最少肯定是减到a的最小值和b的最小值
所以对于每个i先同时减到a和b的最小值,然后再单独减即可
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
int t, a[N], b[55];
int main() {
cin >> t;
while (t--) {
int n;
cin >> n;
int mina = 0x3f3f3f3f, minb = 0x3f3f3f3f;
for (int i = 0; i < n; i++) {
cin >> a[i];
mina = min(mina, a[i]);
}
for (int i = 0; i < n; i++) {
cin >> b[i];
minb = min(minb, b[i]);
}
LL res = 0;
for (int i = 0; i < n; i++) {
int tmp1 = a[i] - mina;
int tmp2 = b[i] - minb;
int tmp3 = min(tmp1, tmp2);
res += tmp3;
tmp1 -= tmp3;
tmp2 -= tmp3;
res += max(tmp1, tmp2);
}
cout << res << endl;
}
return 0;
}
C. Boats Competition
大意:
给出n个数,求一个数s,使得数组中能找到两个数相加等于s的对数最多,输出这个对数
思路:
因为n很小且每个数都小于n,所以可以枚举s,求一共有多少对数相加为s,这里记录每个数出现了多少次,然后直接算即可
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
int t, a[55], num[55];
int main() {
cin >> t;
while (t--) {
int n;
cin >> n;
memset(num, 0, sizeof num);
for (int i = 0; i < n; i++) {
cin >> a[i];
num[a[i]]++;
}
int res = 0;
for (int k = 1; k <= 100; k++) {
int tmp = 0;
for (int i = 1; i <= n; i++) {
for (int j = i; j <= n; j++) {
if (i + j == k) {
if (i == j)
tmp+=num[i] / 2;
else
tmp+=min(num[i],num[j]);
}
}
}
res = max(res, tmp);
}
cout << res << endl;
}
return 0;
}
D. Binary String To Subsequences
大意:
给出一个长度为n的01字符串,问最少分成多少个子序列,使得每个序列都是0和1的交替
思路:
两个队列维护0和1结尾的子序列的编号,对于(s[i]==1),就去维护结尾为0的队列看有没有编号,有的话直接把它删掉,然后加到结尾为1的队列中,否则生成一个新的子序列放到结尾为1的队列中,反之亦然
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
typedef long long LL;
int t, f[N], len[N];
int main() {
cin >> t;
while (t--) {
int n;
cin >> n;
string s;
cin >> s;
int cnt = 0;
queue<int> q0, q1;
for (int i = 0; i < s.size(); i++) {
if (s[i] == '1') {
if (!q0.empty()) {
f[i] = q0.front();
q0.pop();
} else
f[i] = ++cnt;
q1.push(f[i]);
}
if (s[i] == '0') {
if (!q1.empty()) {
f[i] = q1.front();
q1.pop();
} else
f[i] = ++cnt;
q0.push(f[i]);
}
}
cout << cnt << endl;
for (int i = 0; i < s.size(); i++) {
cout << f[i] << ' ';
}
cout << endl;
}
return 0;
}
E1. Weights Division (easy version)
大意:
给出一颗树,现在每次可以将一条边的权除以2,问至少需要几次才可以使得根节点到每个叶节点的权值和小于s
思路:
dfs处理出来每条边经过多少次,然后将每条边修改一次能贡献多少权值加入优先队列,然后每次取出优先队列第一个边,将其除以二再加入队列即可
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
typedef long long LL;
int t;
struct node {
LL cnt, w;
};
LL sum;
LL times[N];
struct cmp {
bool operator()(node a, node b) {
return (a.w - a.w / 2) * a.cnt < (b.w - b.w / 2) * b.cnt;
}
};
vector<pair<LL, LL> > mp[N];
priority_queue<node, vector<node>, cmp> q;
void dfs(int now, int fa, LL noww) {
if (mp[now].size() == 1) times[now]++;
// cout << now << endl;
for (int i = 0; i < mp[now].size(); i++) {
int ne = mp[now][i].first;
LL w = mp[now][i].second;
if (ne == fa) continue;
dfs(ne, now, w);
times[now] += times[ne];
}
node tmp;
tmp.cnt = times[now];
tmp.w = noww;
q.push(tmp);
sum += tmp.cnt * tmp.w;
}
int main() {
cin >> t;
while (t--) {
int n;
LL s;
cin >> n >> s;
while (!q.empty()) q.pop();
for (int i = 1; i <= n; i++) {
mp[i].clear();
times[i] = 0;
}
sum = 0;
for (int i = 0; i < n - 1; i++) {
int x, y;
LL w;
cin >> x >> y >> w;
mp[x].push_back(make_pair(y, w));
mp[y].push_back(make_pair(x, w));
}
dfs(1, 0, 0);
int res = 0;
//cout << sum << endl;
while (sum > s) {
node now = q.top();
q.pop();
//cout << now.cnt << ' ' << now.w << endl;
sum -= now.cnt * (now.w - now.w / 2);
now.w /= 2;
q.push(now);
res++;
}
cout << res << endl;
}
return 0;
}
E2. Weights Division (hard version)
大意:
在E1的基础上,每条边除以二的时候都会有花费,取值是1或2,问最少操作次数
思路:
将花费为1和2的分别放到两个队列中,分别进行E1的操作,分别记录两个队列每次操作可以删去的值
然后遍历队列1,二分找到相应的需要删掉花费为2的操作数量,取min即可
注意两个队列分别可能为空的特判
#include <bits/stdc++.h>
using namespace std;
const int N = 2e6 + 5;
typedef long long LL;
int t;
struct node {
LL cnt, w;
int to, cost;
};
LL sum;
LL times[N];
struct cmp {
bool operator()(node a, node b) {
return (a.w - a.w / 2) * a.cnt < (b.w - b.w / 2) * b.cnt;
}
};
LL sub1[2 * N], sub2[2 * N];
vector<node> mp[N];
priority_queue<node, vector<node>, cmp> q1;
priority_queue<node, vector<node>, cmp> q2;
void dfs(int now, int fa, LL noww, int cost) {
if (mp[now].size() == 1) times[now]++;
// cout << now << endl;
for (int i = 0; i < mp[now].size(); i++) {
int ne = mp[now][i].to;
LL w = mp[now][i].w;
int c = mp[now][i].cost;
if (ne == fa) continue;
dfs(ne, now, w, c);
times[now] += times[ne];
}
node tmp;
tmp.cnt = times[now];
tmp.w = noww;
if (cost == 1)
q1.push(tmp);
else if (cost == 2)
q2.push(tmp);
sum += tmp.cnt * tmp.w;
}
int main() {
cin >> t;
while (t--) {
int n;
LL s;
cin >> n >> s;
while (!q1.empty()) q1.pop();
while (!q2.empty()) q2.pop();
for (int i = 1; i <= n; i++) {
mp[i].clear();
times[i] = 0;
}
sum = 0;
for (int i = 0; i < n - 1; i++) {
int x, y, c;
LL w;
cin >> x >> y >> w >> c;
mp[x].push_back({0, w, y, c});
mp[y].push_back({0, w, x, c});
}
dfs(1, 0, 0, 0);
int step1 = 1;
sub1[0] = 0;
// cout << sum << endl;
LL sum1 = sum;
while (sum1 > s && !q1.empty()) {
node now = q1.top();
q1.pop();
if (now.w == 0) break;
// cout << now.cnt << ' ' << now.w << endl;
sum1 -= now.cnt * (now.w - now.w / 2);
sub1[step1] = sub1[step1 - 1] + now.cnt * (now.w - now.w / 2);
now.w /= 2;
q1.push(now);
step1++;
}
LL sum2 = sum;
int step2 = 1;
sub2[0] = 0;
while (sum2 > s && !q2.empty()) {
node now = q2.top();
q2.pop();
if (now.w == 0) break;
// cout << now.cnt << ' ' << now.w << endl;
sum2 -= now.cnt * (now.w - now.w / 2);
sub2[step2] = sub2[step2 - 1] + now.cnt * (now.w - now.w / 2);
now.w /= 2;
q2.push(now);
step2++;
}
if (sum <= s) {
cout << "0" << endl;
continue;
}
int res = 0x3f3f3f3f;
if (step2 == 1) {
for (int i = 0; i < step1; i++) {
if (sum - sub1[i] <= s) {
res = i;
break;
}
}
} else if (step1 == 1) {
for (int i = 0; i < step2; i++) {
if (sum - sub2[i] <= s) {
res = 2 * i;
break;
}
}
} else {
for (int i = 0; i < step1; i++) {
int pos =
lower_bound(sub2, sub2 + step2, sum - s - sub1[i]) - sub2;
if (pos == step2) continue;
res = min(res, i + 2 * pos);
}
}
cout << res << endl;
}
return 0;
}
F. Yet Another Segments Subset
大意:
给出n个区间,要求最多的一个区间集合,满足集合内任意两个区间,要么是互不相交,要么是完全包含
思路:
区间dp,首先算出和这个区间的左右端点相同的区间数量,然后枚举中点更新即可
注意需要离散化
#include <bits/stdc++.h>
using namespace std;
const int N = 6e3 + 5;
typedef long long LL;
int t;
struct node {
int l, r;
} a[N];
vector<int> v;
int getx(int x) { return lower_bound(v.begin(), v.end(), x) - v.begin(); }
bool cmp(node a, node b) {
if (a.l == b.l) return a.r < b.r;
return a.l < b.l;
}
vector<int> pos[N];
int f[N][N];
int dp(int l, int r) {
if (f[l][r] != -1) return f[l][r];
f[l][r] = 0;
int self = count(pos[l].begin(), pos[l].end(), r);
if (l == r)
f[l][r] = self;
else
f[l][r] = self + dp(l + 1, r);
for (int i = l; i < r; i++) {
f[l][r] = max(self + dp(l, i) + dp(i + 1, r), f[l][r]);
}
return f[l][r];
}
int main() {
cin >> t;
while (t--) {
int n;
cin >> n;
v.clear();
for (int i = 0; i < n; i++) {
int l, r;
cin >> l >> r;
a[i].l = l, a[i].r = r;
v.push_back(l);
v.push_back(r);
}
sort(v.begin(), v.end()), v.erase(unique(v.begin(), v.end()), v.end());
int m = v.size();
for (int i = 0; i < m; i++) {
pos[i].clear();
for (int j = 0; j < m; j++) {
f[i][j] = -1;
}
}
for (int i = 0; i < n; i++) {
a[i].l = getx(a[i].l);
a[i].r = getx(a[i].r);
pos[a[i].l].push_back(a[i].r);
}
cout << dp(0, m - 1) << endl;
}
return 0;
}