Codeforces Round #727 (Div. 2) A~D
阿新 • • 發佈:2021-06-28
A.Contest Start
- 題意:有n名選手進行比賽,第i位選手的開始時間位(i - 1) * x,每位選手比賽的持續時間均為t,例如第1位選手開始比賽時間為:0,結束時間為:t, 第2位選手開始時間為x, 結束時間為x + t。每一位選手的不滿意度為該選手到比賽結束時間, 比賽仍未結束(或者比賽剛開始)選手的數量,求所有選手不滿意度之和。
- 思路:分類討論 + 思維
- 解析:感覺自己的分類討論寫的有點麻煩(比賽的時候瞎搞),這裡記錄一下看到其他大佬的解法。(首先令cnt = t / x)
- 若 cnt >= (n-1)說明當每個人比賽結束時,它身後的所有人都開始比賽(並且仍未結束),此時總的不滿意度為sum(n-1, n-2, n-3 ···, 1),為一個等差數列求和;
- 否則,令num = n - cnt, num表示不滿意度為cnt的人數,相當於第一個到第num個人的不滿意度均為cnt,他們的不滿意度總和為num * cnt,而到第num + 1個人時, 它身後已經沒有cnt個人,所以第num + 1 -> 第n個人的不滿意度之和為(cnt-1, cnt-2, ··· 1)的求和, 兩者再進行相加即可。
- 程式碼:
#include<bits/stdc++.h> using namespace std; typedef long long ll; int k; ll n, x, t; int main() { cin >> k; while(k --) { ll res = 0; cin >> n >> x >> t; ll cnt = t / x; if(cnt >= n - 1) res = n * (n-1) / 2; else { ll num = n - cnt; res = num * cnt + cnt * (cnt-1) / 2; } cout << res << endl; } return 0; }
B.Love Song
- 題意:給出一個由a-z組成的字串str, 若有a則a重複一次, 有b則b重複兩次, 有c則重複三次,例如:abbcb,完成變換後:abbbbcccbb,給定一個[l, r]區間, 求該str在該區間根據上訴規則變換後字串str'的長度
- 思路:字首和
- 解析:將原區間做一個字首和預處理,記錄完成變換操作後整個字串的長度和,最後通過sum(r) - sum(l-1)即為結果。
- 程式碼:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e5 + 5; int n, q; char str[N]; ll sum[N]; int main() { cin >> n >> q; cin >> str + 1; for(int i = 1; i <= n; i++) sum[i] += sum[i-1] + (ll)(str[i] - 'a') + (ll)1; while(q --) { int l, r; cin >> l >> r; cout << sum[r] - sum[l-1] << endl; } return 0; }
C.Stable Groups
- 題意:給出n個學生的成績,若相鄰的兩個學生成績之差不超過x,則該兩個學生可以在一個穩定小組中,並且可以在這n個學生中加入任意成績的k個學生,換言之,也就是將會有n名學生成績已知,可有k名學生成績任意,問這n + k名學生最少能組成幾個穩定小組(k名學生可用可不用,相當於是一個輔助條件,可以幫助組成更少的穩定小組)
- 思路:貪心 + 思維
- 解析:首先將n名學生按成績從小到大進行排序。
- 只有一名學生時特判一下;
- 否則當兩個學生成績差大於x時,按它們之間距離從小到大新增輔助學生(輔助學生就是那k名學生)(貪心思想)
這裡解釋一下程式碼中的num陣列,實際上是這兩名學生中至少需要插入num名輔助學生的數量才能使得這兩名學生在一個穩定小組,最後是根據num從小到大進行排序的,原理實際是一樣的,那重點是這個num的求法,實際上是ceil(dis/x)-1【此處可以自行找規律】, 但這裡會爆double,所以向上取整利用一個巧妙的規律:ceil(dis/x) = (dis + x - 1) / x即可,最後再根據一些條件消耗輔助學生減少穩定小組的數量即可。
- 程式碼:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
ll num[N] ,a[N];
ll n, k, x;
int cnt = 0;
int main()
{
cin >> n >> k >> x;
for(int i = 1; i <= n; i++) cin >> a[i];
sort(a + 1, a + 1 + n);
if(n == 1) cout << 1 << endl;
else
{
for(int i = 2; i <= n; i++)
{
ll dis = a[i] - a[i-1];
if(dis > x)
num[++cnt] = (dis + x - 1) / x - 1;
}
sort(num + 1, num + cnt + 1);
int tcnt = cnt;
for(int i = 1; i <= tcnt && k >= num[i]; i++)
{
k -= num[i];
cnt --;
}
cout << cnt + 1<< endl;
}
return 0;
}
D.PriceFixed
- 題意:需要購買n種商品,每種商品所需購買的數量為ai, 當購買商品的總數量達到bi,則第i件商品價格為原來的價格的一半,求購買n件商品的最小花費。
- 思路:貪心 + 雙指標
- 題解:根據題意,若想要最小花費購買完n種物品,那必然是希望更多的種類物品(準確來說是更多件物品)在購買時能打折,因為購買的單價都一樣,說明每種物品的價值都一樣,那麼我們可以按照bi將物品種類進行排序,也就是先購買容易打折的物品,我們可以設定一個雙指標進行遍歷,將會出現兩種情況:
- 當購買第i種商品可以打折時,直接購買ai件第i種商品即可(此時能打折,不買白不買);
- 當購買第i種商品不能打折時,那就從尾指標r往前遍歷,先購買b較大的商品進行湊數,總商品數量湊夠bi後再購買第i種商品,這樣就儘可能的多購買到打折的商品。
- 程式碼:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
int n;
struct P
{
ll a, b;
}p[N];
bool cmp(P x, P y)
{
return x.b < y.b;
}
int main()
{
ll sum = 0, cnt = 0; //總花費、當前已購買商品的數量
cin >> n;
for(int i = 1; i <= n; i++)
{
cin >> p[i].a >> p[i].b;
sum += p[i].a * (ll)2;
}
sort(p + 1, p + 1 + n, cmp);
int l = 1, r = n;
while(l <= r)
{
if(cnt >= p[l].b) //當前商品所需數量達到打折的數量
{
cnt += p[l].a;
sum -= p[l].a; //打折的商品數量
l ++;
}
else
{
if(p[r].a + cnt >= p[l].b)
{
p[r].a -= p[l].b - cnt;
cnt = p[l].b;
}
else //加上第r種商品仍不滿足第l種商品能打折
{
cnt += p[r].a;
r --;
}
}
}
cout << sum << endl;
return 0;
}