2018-2019 ICPC, NEERC, Southern Subregional Contest (Online Mirror) Solution
瞎扯
又來打比賽了,發現自己菜得不行。
果然我是標準的普及組選手。(這個隊名是啥意思?我也不知道,因為不知道取啥隊名,就隨機在鍵盤上敲Emmm)
這次隊友很給力,把我不會的模擬和貪心全切掉了,並且每次翻譯了正確的題意(我英語真垃圾)。(上次UESTC ACM Final的隊友讓我很絕望,既不會做題又給我翻譯假題面)
然後我把剩下的送分題都切掉了。
於是被碾壓了:
開始通讀全題(提供pdf真良心,避免網絡卡耽誤時間),我覺得很絕望,尤其是那個B,讀得我很絕望,直接扔翻譯裡都棄了。
之後並不知道怎麼做題,然後隨機一道題,發現是水題是一道水題就直接切掉了。
之後就開始看榜,那個題通過的隊多就做哪個。
佬表示這個不是正確的做法,正確的做法是每個人隨機若干道題,先自己去做,不會的話然後扔給隊友。
掛掉的話也可以扔隊友。
但是我和我隊友都挺菜的,這麼搞可能完蛋了。
下來發現B,J都是送分題。
Problem A Find a Number
題目大意
問最小的滿足各位數字之和為$s$並且能被$d$整除的正整數。
首先用bfs確定它的位數,每個狀態記錄通過儘量小的轉移邊轉移的前驅。
然後做完了。
Code
1 /** 2 * Codeforces 3 * Problem#1070AProblem A4 * Accepted 5 * Time: 108ms 6 * Memory: 24800k 7 * Author: yyf 8 */ 9 #include <iostream> 10 #include <cstdlib> 11 #include <cstring> 12 #include <cstdio> 13 #include <queue> 14 using namespace std; 15 typedef bool boolean; 16 17 const int N = 5005, D = 505; 18 #define pii pair<int, int> 19 #define fi first 20 #define sc second 21 22 int d, s; 23 int f[N][D]; 24 char lst[N][D]; 25 int lstr[N][D]; 26 boolean vis[N][D]; 27 28 pii trans(int s, int r, int dig) { 29 return pii(s + dig, (r * 10 + dig) % d); 30 } 31 32 inline void init() { 33 scanf("%d%d", &d, &s); 34 } 35 36 queue<pii> que; 37 boolean bfs() { 38 que.push(pii(0, 0)); 39 memset(f, 0x3f, sizeof(f)); 40 f[0][0] = 0, vis[0][0] = true; 41 while (!que.empty()) { 42 pii e = que.front(); 43 que.pop(); 44 45 for (int i = 0; i <= 9; i++) { 46 pii eu = trans(e.fi, e.sc, i); 47 if (eu.fi > s) 48 break; 49 if (vis[eu.fi][eu.sc]) 50 continue; 51 vis[eu.fi][eu.sc] = true; 52 lst[eu.fi][eu.sc] = i + '0'; 53 lstr[eu.fi][eu.sc] = e.sc; 54 f[eu.fi][eu.sc] = f[e.fi][e.sc] + 1; 55 que.push(eu); 56 } 57 } 58 return vis[s][0]; 59 } 60 61 void print(int s, int r) { 62 if (!s && !r) 63 return ; 64 int nr = lstr[s][r]; 65 print(s - lst[s][r] + '0', nr); 66 putchar(lst[s][r]); 67 } 68 69 70 inline void solve() { 71 if (bfs()) { 72 print(s, 0); 73 } else 74 puts("-1"); 75 } 76 77 int main() { 78 init(); 79 solve(); 80 return 0; 81 }
Problem B Berkomnadzor
題目大意
給定一個IPv4白名單和黑名單,要求用最少的IPv4碼給出一個包含所有黑名單中的地址但不包含任何一個白名單裡的地址。
先判掉無解的情況。
剩下直接分治。
Code
1 /** 2 * Codeforces 3 * Problem#1070B 4 * Accepted 5 * Time: 530ms 6 * Memory: 52800k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 #define ull unsigned long long 13 #define pii pair<int, int> 14 #define ll long long 15 #define ui unsigned 16 #define sc second 17 #define fi first 18 19 const signed ll llf = (signed ll) (~0ull >> 1); 20 const signed int inf = (signed) (~0u >> 1); 21 22 template <typename T> 23 T __abs(T x) { 24 return (x < 0) ? (-x) : (x); 25 } 26 27 template <typename T> 28 void pfill(T* pst, const T* ped, T val) { 29 for ( ; pst != ped; *(pst++) = val); 30 } 31 32 template <typename T> 33 void pcopy(T* pst, const T* ped, T* pv) { 34 for ( ; pst != ped; *(pst++) = *(pv++)); 35 } 36 37 #define digit(_x) ((_x) >= '0' && (_x) <= '9') 38 39 template <typename T> 40 char* read(char* s, T& u) { 41 for ( ; *s && !digit(*s); s++); 42 if (!*s) 43 return NULL; 44 for (u = *s - '0'; ++s, digit(*s); u = u * 10 + *s - '0'); 45 return s; 46 } 47 48 const int N = 2e5 + 5; 49 50 typedef class Segment { 51 public: 52 ui l, r; 53 ui sgn; 54 55 Segment() { } 56 Segment(ui l, ui r, ui sgn):l(l), r(r), sgn(sgn) { } 57 58 boolean operator < (Segment b) const { 59 if (l ^ b.l) 60 return l < b.l; 61 return r < b.r; 62 } 63 64 boolean intersect(Segment b) { 65 return !(b.r < l || b.l > r); 66 } 67 }Segment; 68 69 int n; 70 char buf[100]; 71 72 Segment read() { 73 ui l, r, x; 74 scanf("%s", buf); 75 ui sgn = (buf[0] == '+'); 76 char* str = buf + 1; 77 str = read(str, l); 78 str = read(str, x), l = l << 8 | x; 79 str = read(str, x), l = l << 8 | x; 80 str = read(str, x), l = l << 8 | x; 81 if (*str == '/') { 82 read(str, x); 83 x = 32 - x; 84 l >>= x, l <<= x; 85 if (x == 32) 86 r = ~0u; 87 else 88 r = l | ((1 << x) - 1); 89 return Segment(l, r, sgn); 90 } 91 return Segment(l, l, sgn); 92 } 93 94 int res = 0; 95 vector<Segment> ss, rs; 96 97 inline void init() { 98 scanf("%d", &n); 99 Segment s; 100 for (int i = 1; i <= n; i++) 101 s = read(), ss.push_back(s); 102 } 103 104 void dividing(vector<Segment> &ss, ui l, ui r, ui bit) { 105 if (ss.empty()) 106 return; 107 boolean app1 = false, app0 = false; 108 for (ui i = 0; i < ss.size(); i++) 109 app1 |= (ss[i].sgn == 1), app0 |= (ss[i].sgn == 0); 110 111 if (!app1) { 112 rs.push_back(Segment(l, 32 - bit, 0)); 113 return; 114 } 115 116 if (!app0) 117 return; 118 119 assert(l ^ r); 120 121 ui mid = l + ((r - l) >> 1); 122 vector<Segment> ql, qr; 123 for (ui i = 0; i < ss.size(); i++) { 124 if (ss[i].r <= mid) 125 ql.push_back(ss[i]); 126 else if (ss[i].l > mid) 127 qr.push_back(ss[i]); 128 else { 129 ql.push_back(Segment(ss[i].l, mid, ss[i].sgn)); 130 qr.push_back(Segment(mid + 1, ss[i].r, ss[i].sgn)); 131 } 132 } 133 ss.clear(); 134 dividing(ql, l, mid, bit - 1); 135 dividing(qr, mid + 1, r, bit - 1); 136 } 137 138 inline void solve() { 139 sort(ss.begin(), ss.end()); 140 for (ui i = 0, j; i < ss.size(); i = j) 141 for (j = i + 1; j < ss.size() && ss[j].intersect(ss[i]); j++) 142 if (ss[j].sgn ^ ss[i].sgn) { 143 puts("-1"); 144 return; 145 } 146 dividing(ss, 0, ~0u, 32); 147 printf("%u\n", rs.size()); 148 ui msk = (1 << 8) - 1; 149 for (ui i = 0, x; i < rs.size(); i++) { 150 x = rs[i].l; 151 printf("%u.%u.%u.%u/%u\n", x >> 24, x >> 16 & msk, x >> 8 & msk, x & msk, rs[i].r); 152 } 153 } 154 155 int main() { 156 init(); 157 solve(); 158 return 0; 159 }Problem B
Problem C Cloud Computing
題目大意
一個公司每天需要$K$個CPU核心,有$m$個供應商,在第$l_i$到第$r_i$天,以每個CPU核心$p_i$的價格提供$c_i$個,如果一天買不夠,那麼必須把能夠提供的核心都購買。問最小的總花費。
隨便拿個資料結構就過了。
Code
1 /** 2 * Codeforces 3 * Problem#1070C 4 * Accepted 5 * Time: 249ms 6 * Memory: 28000k 7 * Author: yyf 8 */ 9 #include <algorithm> 10 #include <iostream> 11 #include <cstring> 12 #include <cstdlib> 13 #include <cstdio> 14 #include <vector> 15 using namespace std; 16 typedef bool boolean; 17 18 typedef class Opt { 19 public: 20 int i, p, c; 21 int sgn; 22 23 Opt(int i, int p, int c, int sgn):i(i), p(p), c(c), sgn(sgn) { } 24 25 boolean operator < (Opt b) const { 26 return i < b.i; 27 } 28 }Opt; 29 30 const int bzmax = 21; 31 32 template <typename T> 33 class IndexedTree { 34 public: 35 int s; 36 T* ar; 37 38 IndexedTree() { } 39 IndexedTree(int s):s(s) { 40 ar = new T[(s + 1)]; 41 memset(ar, 0, sizeof(T) * (s + 1)); 42 } 43 44 void add(int idx, T val) { 45 for ( ; idx <= s; idx += (idx & (-idx))) 46 ar[idx] += val; 47 } 48 49 T query(int idx) { 50 T rt = 0; 51 for ( ; idx; idx -= (idx & (-idx))) 52 rt += ar[idx]; 53 return rt; 54 } 55 56 int kth(int k) { 57 int rt = 0, cur = 0; 58 for (int l = (1 << (bzmax - 1)); l; l >>= 1) 59 if ((rt | l) <= s && (cur + ar[rt | l]) < k) 60 rt |= l, cur += ar[rt]; 61 return rt + 1; 62 } 63 }; 64 65 #define ll long long 66 67 const int V = 1e6 + 3; 68 69 int n, K, m; 70 IndexedTree<ll> itc; 71 IndexedTree<ll> its; 72 vector<Opt> vs; 73 74 inline void init() { 75 scanf("%d%d%d", &n, &K, &m); 76 itc = IndexedTree<ll>(V); 77 its = IndexedTree<ll>(V); 78 for (int i = 1, l, r, c, p; i <= m; i++) { 79 scanf("%d%d%d%d", &l, &r, &c, &p); 80 vs.push_back(Opt(l, p, c, 1)); 81 vs.push_back(Opt(r + 1, p, c, -1)); 82 } 83 } 84 85 ll res = 0; 86 inline void solve() { 87 sort(vs.begin(), vs.end()); 88 int pv = 0, s = (signed) vs.size(); 89 for (int i = 1; i <= n; i++) { 90 while (pv < s && vs[pv].i == i) { 91 itc.add(vs[pv].p, vs[pv].c * vs[pv].sgn); 92 its.add(vs[pv].p, vs[pv].c * 1ll * vs[pv].p * vs[pv].sgn); 93 pv++; 94 } 95 ll cnt = itc.query(V); 96 if (cnt < K) { 97 res += its.query(V); 98 } else { 99 int p = itc.kth(K); 100 cnt = itc.query(p); 101 res += its.query(p) - (cnt - K) * p; 102 } 103 } 104 cout << res << endl; 105 } 106 107 int main() { 108 init(); 109 solve(); 110 return 0; 111 }Problem C
Problem D Garbage Disposal
題目大意
每天會產生若干垃圾,每天的垃圾只能當天或者後天處理,第$n + 1$天不能留下垃圾。每次處理垃圾至多能處理$k$個單位,問最少的總處理次數。
隨便模擬一下就過了。
Code
1 //Author: dream_maker 2 #include<bits/stdc++.h> 3 using namespace std; 4 //---------------------------------------------- 5 //typename 6 typedef long long ll; 7 //convenient for 8 #define fu(a, b, c) for (int a = b; a <= c; ++a) 9 #define fd(a, b, c) for (int a = b; a >= c; --a) 10 #define fv(a, b) for (int a = 0; a < (signed)b.size(); ++a) 11 //inf of different typename 12 const int INF_of_int = 1e9; 13 const ll INF_of_ll = 1e18; 14 //fast read and write 15 template <typename T> 16 void Read(T &x) { 17 bool w = 1;x = 0; 18 char c = getchar(); 19 while (!isdigit(c) && c != '-') c = getchar(); 20 if (c == '-') w = 0, c = getchar(); 21 while (isdigit(c)) { 22 x = (x<<1) + (x<<3) + c -'0'; 23 c = getchar(); 24 } 25 if (!w) x = -x; 26 } 27 template <typename T> 28 void Write(T x) { 29 if (x < 0) { 30 putchar('-'); 31 x = -x; 32 } 33 if (x > 9) Write(x / 10); 34 putchar(x % 10 + '0'); 35 } 36 //---------------------------------------------- 37 const int N = 3e5 + 10; 38 ll a[N], n, k, ans = 0; 39 int main() { 40 Read(n), Read(k); 41 fu(i, 1, n) Read(a[i]); 42 fu(i, 1, n - 1) { 43 if (!a[i]) continue; 44 ll num = a[i] / k; 45 if (a[i] % k) ++num; 46 a[i + 1] = max(0ll, a[i + 1] - (num * k - a[i])); 47 ans += num; 48 } 49 if (a[n]) { 50 ans += a[n] / k; 51 if (a[n] % k) ++ans; 52 } 53 Write(ans); 54 return 0; 55 }Problem D
Problem E Getting Deals Done
題目大意
有$n$個任務和引數$m$,每個任務有一個耗時$p_i$,以及時限$t$,要求出$d$使得按照下面方式完成的任務儘量多。
- 按順序完成$p_i \leqslant d$的任務
- 每做$m$個任務需要休息與做這$m$個任務花費的時間相等的時間。
yangkai覺得這玩意兒可以三分,於是我們成功得到了-5.
發現有用的$d$一定時某個$p_i$,可以把$p_i$排序,按順序插進樹狀陣列。
每次可以二分一下求出能夠完成的任務的數量。
時間複雜度$O(n\log^{2} n)$。
但是這個可以二分答案。
判斷條件是是否能夠把所有可做的任務做完。
答案只可能是它或者它加上1後的情況,
因為再大的時候不會花更少的時間去做數量相同的任務。
然後我的垃圾做法就被暴打了。
然後注意幾個地方:
- $p$相同的任務要一起加入樹狀陣列
- 樹狀陣列求第$k$大特判$k = 0$。
Code
1 /** 2 * Codeforces 3 * Problem#1070E 4 * Accepted 5 * Time: 608ms 6 * Memory: 4700k 7 */ 8 #include <algorithm> 9 #include <iostream> 10 #include <cstring> 11 #include <cstdlib> 12 #include <cstdio> 13 #ifndef WIN32 14 #define Auto "%lld" 15 #else 16 #define Auto "%I64d" 17 #endif 18 using namespace std; 19 typedef bool boolean; 20 #define ll long long 21 22 template <typename T> 23 class IndexedTree { 24 public: 25 int s; 26 T* ar; 27 28 IndexedTree() { } 29 IndexedTree(int s):s(s) { 30 ar = new T[(s + 1)]; 31 memset(ar, 0, sizeof(T) * (s + 1)); 32 } 33 34 void add(int idx, T val) { 35 for ( ; idx <= s; idx += (idx & (-idx))) 36 ar[idx] += val; 37 } 38 39 T query(int idx) { 40 T rt = 0; 41 for ( ; idx; idx -= (idx & (-idx))) 42 rt += ar[idx]; 43 return rt; 44 } 45 46 int kth(int k) { 47 if (!k) 48 return 0; 49 int rt = 0, cur = 0; 50 for (int i = (1 << 18); i; i >>= 1) 51 if ((rt | i) <= s && ar[rt | i] + cur < k) 52 rt |= i, cur += ar[