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;
}