Codeforces Round #466 (Div. 2) Solution

Codeforces 940A Points on the line

題目大意c++

  定義一個可重集的距離是它中間最大的兩個數之間的差,特殊地,只有一個元素的可重集的距離爲0。數組

  給定一個可重集,問最少刪掉多少個數使得它的距離小於等於d。ide

  排序後單調指針掃,或者直接開桶計數。spa

Code

 1 /**  2  * Codeforces  3  * Problem#940A  4  * Accepted  5  * Time: 15ms  6  * Memory: 2000k  7  */ 
 8 #include <bits/stdc++.h>
 9 using namespace std; 10 typedef bool boolean; 11 
12 int n, d; 13 int res; 14 int* ar; 15 
16 inline void init() { 17     scanf("%d%d", &n, &d); 18     ar = new int[(n + 1)]; 19     for (int i = 1; i <= n; i++) 20         scanf("%d", ar + i); 21 } 22 
23 inline void solve() { 24     sort (ar + 1, ar + n + 1); 25     int r = 1; 26     res = n - 1; 27     for (int i = 1; i <= n; i++) { 28         while (r < n && ar[r + 1] - ar[i] <= d) r++; 29         res = min(res, i + (n - r) - 1); 30  } 31     printf("%d", res); 32 } 33 
34 int main() { 35  init(); 36  solve(); 37     return 0; 38 }
Problem A

Codeforces 940B Our Tanya is Crying Out Loud

題目大意指針

  給定一個數$n$和$k$。你有兩個操做能夠進行code

  1. 將$n$減去1,花費$a$。
  2. 當$n$是$k$的倍數的時候,將$n$除以$k$,花費$b$。

  問將$n$變爲1的最小花費blog

  當 $k = 1$ 的時候特判。排序

  當$n$爲$k$的倍數的時候,比較直接除和直接減到$\frac{n}{k}$的花費,若是前者更優就除,不然直接減到1。隊列

  當$n$不爲$k$的倍數的時候,直接減到$\left \lfloor\frac{n}{k}  \right \rfloor k$。字符串

  考慮若是在 $ik$ 的決策不是減到 $i$ 或者直接除 $k$ 獲得 $i$,那麼考慮必定是作了若干次減法操做獲得 $p$ 而後再除以 $k$ 獲得 $x$。不難證實先除以 $k$ 獲得 $l$,再作若干次減法操做獲得 $x$ 不會更劣。

Code

 1 /**  2  * Codeforces  3  * Problem#940B  4  * Accepted  5  * Time: 30ms  6  * Memory: 2000k  7  */ 
 8 #include <bits/stdc++.h>
 9 #ifndef WIN32 10 #define Auto "%lld"
11 #else
12 #define Auto "%I64d"
13 #endif
14 using namespace std; 15 
16 #define ll long long
17 
18 ll n, k, a, b, res = 0; 19 
20 inline void init() { 21     scanf(Auto""Auto""Auto""Auto, &n, &k, &a, &b); 22 } 23 
24 inline void solve() { 25     if (k == 1) { 26         res = (n - 1) * a; 27     } else { 28         while (n != 1) { 29             if (n < k) { 30                 res += (n - 1) * a; 31                 break; 32             } else { 33                 res += min((n % k) * a + b, (n - n / k) * a); 34                 n /= k; 35  } 36  } 37  } 38     printf(Auto"\n", res); 39 } 40 
41 int main() { 42  init(); 43  solve(); 44     return 0; 45 }
Problem B

Codeforces 940C Phone Numbers

題目大意

  給定一個由小寫字母組成且長度爲$n$的字符串$s$,要求輸出一個字符串$t$,知足:

  1. $t$中出現的字符在$s$中也出現過
  2. 它的長度爲$k$
  3. $t$的字典序大於$s$
  4. 在知足上述條件的全部串中,是字典序最小的一個

  題目保證存在解。

  當$k > n$時,直接輸出字符串$s$,再補上$s$中最小的字符。

  不然找到$s$中從第$k$個字符往前找第一個不是最大的字符,把它變爲略比它的字符。

  而後後面的補最小的字符。

Code

 1 /**  2  * Codeforces  3  * Problem#940C  4  * Accepted  5  * Time: 31ms  6  * Memory: 2100k  7  */ 
 8 #include <bits/stdc++.h>
 9 using namespace std; 10 typedef bool boolean; 11 
12 int n, k; 13 int mi = 0, ma = 25; 14 boolean exist[26]; 15 char str[100005]; 16 
17 inline void init() { 18     scanf("%d%d", &n, &k); 19     memset(exist, false, sizeof(exist)); 20     scanf("%s", str + 1); 21 } 22 
23 inline void solve() { 24     for (int i = 1; i <= n; i++) 25         exist[str[i] - 'a'] = true; 26     for (mi = 0; !exist[mi]; mi++); 27     for (ma = 25; !exist[ma]; ma--); 28     if (n < k) { 29         printf("%s", str + 1); 30         for (int i = n + 1; i <= k; i++) 31             putchar(mi + 'a'); 32         return; 33  } 34     int p; 35     for (p = k; p && str[p] == ma + 'a'; p--); 36     str[p]++; 37     while (!exist[str[p] - 'a']) 38         str[p]++; 39     for (p = p + 1; p <= k; p++) 40         str[p] = mi + 'a'; 41     str[k + 1] = 0; 42     puts(str + 1); 43 } 44 
45 int main() { 46  init(); 47  solve(); 48     return 0; 49 }
Problem C

Codeforces 940D Alena And The Heater

題目大意

  給定一個數組$a$,和一個01串$b$,$b$按照以下方式生成(請自行閱讀英文)

  

  請你找出一組合法的$l, r$,保證輸入存在解。

  發現生成方式比較特殊。

  因而根據生成方式更新$l, r$便可。

Code

 1 /**  2  * Codeforces  3  * Problem#940D  4  * Accepted  5  * Time: 31ms  6  * Memory: 2500k  7  */ 
 8 #include <bits/stdc++.h>
 9 using namespace std; 10 
11 int n; 12 int a[100005]; 13 char b[100005]; 14 
15 inline void init() { 16     scanf("%d", &n); 17     for (int i = 1; i <= n; i++) 18         scanf("%d", a + i); 19     scanf("%s", b + 1); 20 } 21 
22 char sameNumber(int p) { 23     char x = b[p - 1]; 24     for (int i = 2; i <= 4; i++) 25         if (b[p - i] != x) 26             return '2'; 27     return x; 28 } 29 
30 int l = -1e9, r = 1e9; 31 
32 inline void solve() { 33     for (int i = 5; i <= n; i++) { 34         char c = sameNumber(i); 35         if (c == '2')    continue; 36         if (c == b[i])    continue; 37         if (c == '1') { 38             for (int j = 0; j <= 4; j++) 39                 r = min(r, a[i - j] - 1); 40         } else { 41             for (int j = 0; j <= 4; j++) 42                 l = max(l, a[i - j] + 1); 43  } 44  } 45     printf("%d %d", l, r); 46 } 47 
48 int main() { 49  init(); 50  solve(); 51     return 0; 52 }
Problem D

Codeforces 940E Cashback

題目大意

  給定一個長度爲$n$的數組和常數$c$,要求你對它進行劃分。劃分長度爲$k$一段的代價是其中全部除去前$\left \lfloor \frac{k}{c} \right \rfloor$小的數的和。定義一個劃分的代價是劃分的全部段的代價的和。

  問最小的代價和。

  一段代價能夠表示爲這一段的和減去前$\left \lfloor \frac{k}{c} \right \rfloor$小的數的和。

  那麼考慮兩段連續的長度爲$c$的合併到一塊兒,這樣會使得前2小的和變小。因此劃分的最長的長度不會超過$2c - 1$

  那麼考慮長度在$c + 1$和$2c - 1$之間的段。我只須要留一個長度爲$c$的段,把它和剩下的分開。這樣看的話,劃分的一段的長度不會超過$c$。

  繼續考慮長度小於$c$的段,它的代價直接就是它中間的元素的和。所以能夠直接把它分紅一些只有1個元素的段。

  所以存在一種最優方案知足劃分的每一段長度不是1就是$c$。

  所以我只用維護連續$c$個元素的最小值,以及它們的和。

  因而上單調隊列。

  時間複雜度$O(n)$。

Code

 1 /**  2  * Codeforces  3  * Problem#940E  4  * Accepted  5  * Time: 31ms  6  * Memory: 3700k  7  */ 
 8 #include <bits/stdc++.h>
 9 #ifndef WIN32 10 #define Auto "%lld"
11 #else
12 #define Auto "%I64d"
13 #endif
14 using namespace std; 15 
16 #define ll long long
17 
18 const int N = 100005; 19 
20 int n, c; 21 int st = 1, ed = 0; 22 ll f[N]; 23 int ar[N]; 24 int que[N]; 25 
26 inline void init() { 27     scanf("%d%d", &n, &c); 28     for (int i = 1; i <= n; i++) 29         scanf("%d", ar + i); 30 } 31 
32 inline void solve() { 33     
34     f[1] = 0; 35     ll sum = 0; 36     for (int i = 1; i <= n; i++) { 37         while (ed >= st && ar[que[ed]] >= ar[i])    ed--; 38         que[++ed] = i; 39         f[i] = f[i - 1] + ar[i]; 40         sum += ar[i]; 41         while (ed >= st && que[st] <= i - c)    st++; 42         if (i >= c) 43             sum -= ar[i - c], f[i] = min(f[i], f[i - c] + sum - ar[que[st]]); 44  } 45     
46     printf(Auto"\n", f[n]); 47 } 48 
49 int main() { 50  init(); 51  solve(); 52     return 0; 53 }
Problem E

Codeforces 940F Machine Learning

題目大意

  給定一個長度爲$n$的數組,要求支持兩個操做:

  1. 詢問一個區間中全部數的出現次數組成的集合的mex。
  2. 修改一個位置上的數。

  一個可重集合的mex是這個可重集合中最小的不存在的非負整數。

  這種毒瘤題?我也不會作。那就直接莫隊好了。

  什麼?要修改?那就帶修莫隊好了。

  惟一的問題是如何求一個集合的mex?

  考慮是出現次數,因此答案不會超過500(由於$C_{500}^{2}= \frac{500 \times 499}{2}>10^{5}$),超過500次的出現次數直接扔,查詢操做暴力for。

  時間複雜度$O\left(n^{\frac{5}{3}}\right)$

Code

 1 /**  2  * Codeforces  3  * Problem#940F  4  * Accepted  5  * Time: 1872ms  6  * Memory: 11700k  7  */
 8 #include <bits/stdc++.h>
 9 using namespace std;  10 typedef bool boolean;  11 
 12 #define pii pair<int, int>
 13 #define fi first
 14 #define sc second
 15 
 16 const int cs3 = 2500, cs2 = 450;  17 
 18 typedef class Query {  19     public:  20         int l, r, t;  21         int id;  22 
 23         Query(int l = 0, int r = 0, int t = 0, int id = 0):l(l), r(r), t(t), id(id) { }  24 
 25         boolean operator < (Query b) const {  26             if (l / cs3 != b.l / cs3)    return l < b.l;  27             if (r / cs3 != b.r / cs3)    return r < b.r;  28             return t < b.t;  29  }  30 }Query;  31 
 32 int n, m;  33 int cm = 0, cq = 0, cd = 0;  34 int* ar;  35 Query* qs;  36 pii *ops;  37 int cnt[200005];  38 int exist[200005];  39 int cover[cs2];  40 int *res;  41 map<int, int> mp;  42 
 43 int alloc(int x) {  44     if (mp.count(x))    return mp[x];  45     return mp[x] = ++cd;  46 }  47 
 48 inline void init() {  49     scanf("%d%d", &n, &m);  50     ar = new int[(n + 1)];  51     ops = new pii[(m + 1)];  52     qs = new Query[(m + 1)];  53        res = new int[(m + 1)];  54     for (int i = 1; i <= n; i++)  55         scanf("%d", ar + i), ar[i] = alloc(ar[i]);  56     for (int i = 1, opt, l, r; i <= m; i++) {  57         scanf("%d%d%d", &opt, &l, &r);  58         if (opt == 1)  59             ++cq, qs[cq] = Query(l, r, cm, cq);  60         else
 61             r = alloc(r), ops[++cm] = pii(l, r);  62  }  63     exist[0] = 211985, cover[0] = 1;  64 }  65 
 66 inline void update(int x, int sign) {  67     if (!exist[x] && sign == 1)    cover[x / cs2]++;  68     exist[x] += sign;  69     if (!exist[x] && sign == -1)cover[x / cs2]--;  70 }  71 
 72 inline void updatec(int p, int sign) {  73     int x = ar[p];  74     update(cnt[x], -1);  75     cnt[x] += sign;  76     update(cnt[x], 1);  77 }  78 
 79 inline void update(int op, int l, int r) {  80     int p = ops[op].fi;  81     if (l <= p && p <= r)  82         updatec(p, -1);  83  swap(ar[p], ops[op].sc);  84     if (l <= p && p <= r)  85         updatec(p, 1);  86 }  87 
 88 inline void solve() {  89     sort(qs + 1, qs + cq + 1);  90     int mdzzl = 1, mdzzr = 0, mdzzt = 0;  91     for (int i = 1; i <= cq; i++) {  92         while (mdzzr < qs[i].r)    updatec(++mdzzr, 1);  93         while (mdzzr > qs[i].r)    updatec(mdzzr--, -1);  94         while (mdzzl < qs[i].l)    updatec(mdzzl++, -1);  95         while (mdzzl > qs[i].l)    updatec(--mdzzl, 1);  96         while (mdzzt < qs[i].t)    update(++mdzzt, mdzzl, mdzzr);  97         while (mdzzt > qs[i].t)    update(mdzzt--, mdzzl, mdzzr);  98 
 99         for (int j = 0; j < cs2; j++) { 100             if (cover[j] < cs2) { 101                 int k = j * cs2; 102                 while (exist[k])    k++; 103                 res[qs[i].id] = k; 104                 break; 105  } 106  } 107  } 108 
109     for (int i = 1; i <= cq; i++) 110         printf("%d\n", res[i]); 111 } 112 
113 int main() { 114  init(); 115  solve(); 116     return 0; 117 }
Problem F
相關文章
相關標籤/搜索