2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018) Solution
A. Altruistic Amphibians
Upsolved.
题意:
$有n只青蛙,其属性用三元组表示 <l_i, w_i, h_i> l_i是它能跳的高度,w_i是它的体重,h_i是它的身高$
一只青蛙的承重不能超过它的体重,它可以踩在别的青蛙上面跳
一口井的深度为$d, 一只青蛙能够跳出去当且仅当它离井口的距离严格小于它的l_i$
$离井口的距离为d - 它所踩的青蛙的身高和,当然可以不踩其他青蛙$
求最多跳出去多少只青蛙
思路:
显然,重量最大的青蛙肯定只能在最下面
那么按重量大小排个序
然后考虑递推到下面
$dp[i] 表示 重量为i的青蛙最高能处在什么样的高度$
有
$dp[j - a[i].w] = dp[j] +a[i].h \;\;j \in [a[i].w, 2 * a[i].w)$
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 #define N 100010 6 const int D = (int)1e8 + 10; 7 int n, d; 8 struct node 9 { 10 int l, w, h; 11 void scan() { scanf("%d%d%d", &l, &w, &h); } 12 bool operator < (const node &r) const { return w > r.w; } 13 }a[N]; 14 int dp[D]; 15 16 int main() 17 { 18 while (scanf("%d%d", &n, &d) != EOF) 19 { 20 for (int i = 1; i <= n; ++i) a[i].scan(); 21 sort(a + 1, a + 1 + n); 22 int res = 0; 23 for (int i = 1; i <= n; ++i) 24 { 25 if (dp[a[i].w] + a[i].l > d) ++res; 26 for (int j = a[i].w; j < min(2 * a[i].w, (int)1e8 + 2); ++j) 27 dp[j - a[i].w] = max(dp[j - a[i].w], dp[j] + a[i].h); 28 } 29 printf("%d\n", res); 30 } 31 return 0; 32 }
B. Baby Bites
Solved.
签到。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int n; 5 int pre, now; 6 string s; 7 8 int f() 9 { 10 if (s == "mumble") return -1; 11 int res = 0; 12 for (int i = 0, len = s.size(); i < len; ++i) res = res * 10 + s[i] - '0'; 13 return res; 14 } 15 16 bool solve() 17 { 18 bool flag = 1; 19 pre = 0; 20 for (int i = 1; i <= n; ++i) 21 { 22 cin >> s; 23 now = f(); 24 if (now == -1) ++pre; 25 else if (now != pre + 1) flag = 0; 26 else pre = now; 27 } 28 return flag; 29 } 30 31 int main() 32 { 33 ios::sync_with_stdio(false); 34 cin.tie(0); cout.tie(0); 35 while (cin >> n) cout << (solve() ? "makes sense" : "something is fishy") << "\n"; 36 return 0; 37 }
C. Code Cleanups
Solved.
签到。
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 int n; 6 int vis[400]; 7 8 int main() 9 { 10 while(~scanf("%d", &n)) 11 { 12 memset(vis, 0, sizeof vis); 13 for(int i = 1, num; i <= n; ++i) 14 { 15 scanf("%d", &num); 16 vis[num]++; 17 } 18 int ans = 0; 19 int tmp = 0; 20 int pre = 0; 21 for(int i = 1; i <= 365; ++i) 22 { 23 tmp += vis[i]; 24 pre += tmp; 25 if(pre >= 20) 26 { 27 ++ans; 28 pre = 0; 29 tmp = 0; 30 } 31 } 32 if(pre) ans++; 33 printf("%d\n", ans); 34 } 35 return 0; 36 }
D. Delivery Delays
Upsolved.
题意:
有一张无向图,顶点1有一家披萨店,时不时会有一点发布订单表示它在$s_i时刻要一份披萨,这份披萨要在t_i时刻才能好$
$只有一个送货员,必须满足先到先服务的原则,如何安排送货流程使得等待时间最长的顾客的等待时间最短$
思路:
先预处理出任意两点之间的最短路径,再考虑二分答案
$用dp验证$
$dp[i][j][k] 表示已经送完前i个点,拿到前j个披萨,k \in [0, 1]$
$0 表示 目前处于第i个点,1表示目前处于披萨店$
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 #define N 1010 6 #define INFLL 0x3f3f3f3f3f3f3f3f 7 struct Graph 8 { 9 struct node 10 { 11 int to, nx, w; 12 node () {} 13 node (int to, int nx, int w) : to(to), nx(nx), w(w) {} 14 }a[N << 5]; 15 int head[N], pos; 16 void init() 17 { 18 memset(head, -1, sizeof head); 19 pos = 0; 20 } 21 void add(int u, int v, int w) 22 { 23 a[++pos] = node(v, head[u], w); head[u] = pos; 24 a[++pos] = node(u, head[v], w); head[v] = pos; 25 } 26 }G; 27 #define erp(u) for (int it = G.head[u], v = G.a[it].to, w = G.a[it].w; ~it; it = G.a[it].nx, v = G.a[it].to, w = G.a[it].w) 28 29 int n, m, q; 30 ll dist[N][N]; 31 namespace Dij 32 { 33 struct node 34 { 35 int to; ll w; 36 node () {} 37 node (int to, ll w) : to(to), w(w) {} 38 bool operator < (const node &r) const { return w > r.w; } 39 }; 40 bool used[N]; 41 void run() 42 { 43 for (int st = 1; st <= n; ++st) 44 { 45 for (int i = 1; i <= n; ++i) dist[st][i] = INFLL, used[i] = false; 46 priority_queue <node> q; q.emplace(st, 0); dist[st][st] = 0; 47 while (!q.empty()) 48 { 49 int u = q.top().to; q.pop(); 50 if (used[u]) continue; 51 used[u] = 1; 52 erp(u) if (!used[v] && dist[st][v] > dist[st][u] + w) 53 { 54 dist[st][v] = dist[st][u] + w; 55 q.emplace(v, dist[st][v]); 56 } 57 } 58 } 59 } 60 } 61 62 ll dp[N][N][2]; 63 // dp[i][j][0] 表示已经送完前i个点,拿到前j个披萨,目前处于第i个点的最小时间 64 // dp[i][j][1] 表示已经送完前i个点,拿到前j个披萨,目前处于披萨店的最小时间 65 int s[N], u[N], t[N]; 66 bool check(ll x) 67 { 68 memset(dp, 0x3f, sizeof dp); 69 dp[0][0][1] = 0; 70 for (int i = 0; i < q; ++i) 71 { 72 for (int j = i; j <= q; ++j) 73 { 74 for (int o = 0; o < 2; ++o) 75 { 76 if (!o) 77 { 78 dp[i][j][1] = min(dp[i][j][1], dp[i][j][0] + 1ll * dist[u[i]][1]); 79 if (j > i) 80 { 81 ll T = dp[i][j][0] + dist[u[i]][u[i + 1]]; 82 if (s[i + 1] + x >= T) 83 dp[i + 1][j][0] = min(dp[i + 1][j][0], T); 84 } 85 } 86 else 87 { 88 if (j < q) 89 dp[i][j + 1][1] = min(dp[i][j + 1][1], max(dp[i][j][1], 1ll * t[j + 1])); 90 if (j > i) 91 { 92 ll T = dp[i][j][1] + dist[1][u[i + 1]]; 93 if (s[i + 1] + x >= T) 94 dp[i + 1][j][0] = min(dp[i + 1][j][0], T); 95 } 96 } 97 } 98 } 99 } 100 return dp[q][q][0] != INFLL; 101 } 102 103 int main() 104 { 105 while (scanf("%d%d", &n, &m) != EOF) 106 { 107 G.init(); 108 for (int i = 1, u, v, w; i <= m; ++i) 109 { 110 scanf("%d%d%d", &u, &v, &w); 111 G.add(u, v, w); 112 } 113 Dij::run(); 114 scanf("%d", &q); 115 for (int i = 1; i <= q; ++i) scanf("%d%d%d", s + i, u + i, t + i); 116 ll l = 0, r = INFLL, res = -1; 117 while (r - l >= 0) 118 { 119 ll mid = (l + r) >> 1; 120 if (check(mid)) 121 { 122 res = mid; 123 r = mid - 1; 124 } 125 else 126 l = mid + 1; 127 } 128 printf("%lld\n", res); 129 } 130 return 0; 131 }
E. Explosion Exploit
Solved.
题意:
你有$n个随从,对方有m个随从,你现在可以发动一个技能,造成d点伤害$
$每点伤害都会等概率的下落到敌方的随从身上,求发动一次技能,敌方随从全部死亡的概率$
思路:
概率记忆化搜索
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 typedef long long ll; 6 7 int n, m, d; 8 int sum[3][10]; 9 map <ll, double>dp; 10 11 ll calc() 12 { 13 ll res = 0; 14 for(int i = 1; i <= 6; ++i) 15 { 16 res += sum[0][i]; 17 res *= 10; 18 } 19 for(int i = 1; i <= 6; ++i) 20 { 21 res += sum[1][i]; 22 res *= 10; 23 } 24 return res; 25 } 26 27 double DFS(ll S, int limit) 28 { 29 if(dp.count(S)) return dp[S]; 30 int cnt = 0; 31 for(int i = 1; i <= 6; ++i) cnt += sum[1][i]; 32 if(!cnt) return 1; 33 if(limit == 0) return 0; 34 for(int i = 1; i <= 6; ++i) cnt += sum[0][i]; 35 double res = 0; 36 for(int i = 0; i < 2; ++i) 37 { 38 for(int j = 1; j <= 6; ++j) 39 { 40 if(sum[i][j] == 0) continue; 41 sum[i][j]--; 42 sum[i][j - 1]++; 43 double tmp = DFS(calc(), limit - 1); 44 sum[i][j]++; 45 sum[i][j - 1]--; 46 res += 1.0 * sum[i][j] / cnt * tmp; 47 } 48 } 49 dp[S] = res; 50 return res; 51 } 52 53 int main() 54 { 55 while(~scanf("%d %d %d", &n, &m, &d)) 56 { 57 dp.clear(); 58 memset(sum, 0, sizeof sum); 59 for(int i = 1, num; i <= n; ++i) 60 { 61 scanf("%d", &num); 62 sum[0][num]++; 63 } 64 for(int i = 1, num; i <= m; ++i) 65 { 66 scanf("%d", &num); 67 sum[1][num]++; 68 } 69 double ans = DFS(calc(), d); 70 printf("%.10f\n", ans); 71 } 72 return 0; 73 }
F. Firing the Phaser
Unsolved.
G. Game Scheduling
Unsolved.
H. House Lawn
Solved.
题意:
有一些割草机,工作一段时间,休息一段时间
求哪些机器在T周至少割草T次,如果有,按输入顺序输出价格最低的
思路:
因为$周期是LCM(t + r, 10080), 如果这个周期内是可以的,那就是可以的$
因为如果中间有一周割不完了,那么后面肯定是割不完的
因为开始的局面相当于满电让你割,那么对于后面的情况
每周平均割草的次数肯定小于等于前面的
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 110 5 #define ll long long 6 7 ll l; int m; 8 ll Min; 9 10 ll gcd(ll a, ll b) 11 { 12 return b == 0 ? a : gcd(b, a % b); 13 } 14 15 ll lcm(ll a, ll b) 16 { 17 return a * b / gcd(a, b); 18 } 19 20 struct qnode 21 { 22 string name; 23 ll p, c, t, r; 24 bool flag; 25 void scan() 26 { 27 flag = false; 28 p = 0, c = 0, t = 0, r = 0; 29 name = ""; 30 string s; 31 getline(cin, s); 32 int i, len = s.size(); 33 for (i = 0; s[i] != ','; ++i) 34 name += s[i]; 35 for (++i; s[i] != ','; ++i) p = p * 10 + s[i] - '0'; 36 for (++i; s[i] != ','; ++i) c = c * 10 + s[i] - '0'; 37 for (++i; s[i] != ','; ++i) t = t * 10 + s[i] - '0'; 38 for (++i; i < len; ++i) r = r * 10 + s[i] - '0'; 39 //cout << p << " " << c << " " << t << " " << r << endl; 40 } 41 void f() 42 { 43 ll need = l % (c * t) == 0 ? l / (c * t) : l / (c * t) + 1; 44 ll remind = 0; 45 if (l % (c * t)) remind = t - (l - (c * t) * (need)) % c; 46 ll tot = need * (t + r); 47 tot -= remind; 48 flag = tot <= 10080; 49 if (flag) Min = min(Min, p); 50 } 51 void F4() 52 { 53 flag = false; 54 ll circle = lcm(t + r, 10080); 55 ll T = circle / 10080; 56 if(circle / (t + r) * c * t>= T * l) 57 { 58 flag = true; 59 Min = min(Min, p); 60 } 61 } 62 }qarr[N]; 63 64 int main() 65 { 66 ios::sync_with_stdio(false); 67 cin.tie(0); cout.tie(0); 68 while (cin >> l >> m) 69 { 70 string s; 71 getline(cin, s); 72 Min = 0x3f3f3f3f3f3f3f3f; 73 for (int i = 1; i <= m; ++i) qarr[i].scan(), qarr[i].F4(); 74 vector <string> res; 75 for (int i = 1; i <= m; ++i) if (qarr[i].flag && qarr[i].p == Min) res.push_back(qarr[i].name); 76 for (auto it : res) cout << it << "\n"; 77 if (res.empty()) cout << "no such mower\n"; 78 } 79 return 0; 80 }
I. Intergalactic Bidding
Solved.
简单模拟。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 1010 5 #define DLEN 4 6 #define MAXN 9999 7 int n; 8 9 class BigNum 10 { 11 private: 12 int a[N]; 13 int len; 14 public: 15 BigNum() { len = 1; memset(a, 0, sizeof a); } 16 BigNum(const char *s) 17 { 18 int t, k, index, L, i; 19 memset(a, 0, sizeof a); 20 L = strlen(s); 21 len = L / DLEN; 22 if (L % DLEN) ++len; 23 index = 0; 24 for (int i = L - 1; i >= 0; i -= DLEN) 25 { 26 t = 0; 27 k = i - DLEN + 1; 28 if (k < 0) k = 0; 29 for (int j = k; j <= i; ++j) 30 t = t * 10 + s[j] - '0'; 31 a[index++] = t; 32 } 33 } 34 bool operator == (const BigNum &T) const 35 { 36 if (len != T.len) return false; 37 for (int i = 0; i < len; ++i) if (a[i] != T.a[i]) 38 return false; 39 return true; 40 } 41 bool operator < (const BigNum &T) const 42 { 43 if (len != T.len) return len < T.len; 44 for (int i = len - 1; i >= 0; --i) if (a[i] != T.a[i]) 45 return a[i] < T.a[i]; 46 return true; 47 } 48 BigNum& operator = (const BigNum &n) 49 { 50 int i; 51 len = n.len; 52 memset(a, 0, sizeof a); 53 for (int i = 0; i < len; ++i) a[i] = n.a[i]; 54 return *this; 55 } 56 BigNum operator - (const BigNum &T) const 57 { 58 int i, j, big; 59 BigNum t1, t2; 60 t1 = *this, t2 = T; 61 big = t1.len; 62 for (i = 0; i < big; ++i) 63 { 64 65 if (t1.a[i] < t2.a[i]) 66 { 67 j = i + 1; 68 while (t1.a[j] == 0) ++j; 69 t1.a[j--]--; 70 while (j > i) t1.a[j--] += MAXN; 71 t1.a[i] += MAXN + 1 - t2.a[i]; 72 } 73 else t1.a[i] -= t2.a[i]; 74 } 75 t1.len = big; 76 while (t1.a[t1.len - 1] == 0 && t1.len > 1) 77 { 78 t1.len--; 79 big--; 80 } 81 return t1; 82 } 83 void print() 84 { 85 cout << a[len - 1]; 86 for (int i = len - 2; i >= 0; --i) 87 printf("%04d", a[i]); 88 puts(""); 89 } 90 }; 91 92 char str[N]; 93 BigNum s; 94 95 struct node 96 { 97 string name; 98 BigNum val; 99 void scan() 100 { 101 cin >> name; 102 cin >> str; 103 val = BigNum(str); 104 } 105 bool operator < (const node &r) { return r.val < val; } 106 }arr[N]; 107 108 vector <string> res; 109 int main() 110 { 111 ios::sync_with_stdio(false); 112 cin.tie(0); cout.tie(0); 113 while (cin >> n) 114 { 115 res.clear(); 116 cin >> str; s = BigNum(str); 117 for (int i = 1; i <= n; ++i) arr[i].scan(); 118 sort(arr + 1, arr + 1 + n); 119 for (int i = 1; i <= n; ++i) if (arr[i].val < s) 120 { 121 s = s - arr[i].val; 122 res.push_back(arr[i].name); 123 } 124 if (!(s == BigNum("0"))) cout << "0\n"; 125 else 126 { 127 cout << res.size() << "\n"; 128 for (auto it : res) cout << it << "\n"; 129 } 130 } 131 return 0; 132 }
J. Jumbled String
Solved.
题意:
要求构造一个01串,使得所有子序列中,$00出现a次,01出现b次,10出现c次,11出现d次$
思路:
显然,如果$a > 0, b > 0, 那么其中0的个数和1的个数是固定的$
而且有$b + c == cnt[0] \cdot cnt[1]$
$此处cnt 表示 0的个数或者1的个数$
$那么刚开始0全放全面,1全放后面,然后把1往前面挪动,直到满足条件$
记得特判几种特殊情况以及$Impossible$
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 ll a, b, c, d; 6 ll n, m, need, remind; 7 8 ll f(ll x) 9 { 10 for (ll i = 1; ; ++i) 11 { 12 if ((i * (i - 1) / 2) > x) return -1; 13 if (i * (i - 1) / 2 == x) return i; 14 } 15 } 16 17 int main() 18 { 19 while (scanf("%lld%lld%lld%lld", &a, &b, &c, &d) != EOF) 20 { 21 if (!a && !b && !c && !d) puts("0"); 22 else if (!b && !c && !d) 23 { 24 n = f(a); 25 if (n == -1) puts("impossible"); 26 else 27 { 28 for (int i = 1; i <= n; ++i) putchar('0'); 29 puts(""); 30 } 31 } 32 else if (!a && !b && !c) 33 { 34 m = f(d); 35 if (m == -1) puts("impossible"); 36 else 37 { 38 for (int i = 1; i <= m; ++i) putchar('1'); 39 puts(""); 40 } 41 } 42 else 43 { 44 n = f(a), m = f(d); 45 if (n == -1 || m == -1 || n * m != (b + c)) puts("impossible"); 46 else 47 { 48 need = b / n; 49 remind = b % n; 50 for (int i = 1; i <= m - need - (remind ? 1 : 0); ++i) putchar('1'); 51 for (int i = 1; i <= n; ++i) 52 { 53 putchar('0'); 54 if (i == remind) putchar('1'); 55 } 56 for (int i = 1; i <= need; ++i) putchar('1'); 57 puts(""); 58 } 59 } 60 } 61 return 0; 62 }
K. King's Colors
Solved.
题意:
给出一棵树,相邻结点不能染相同颜色,求恰好染k种颜色的方案数。
思路:
因为是一棵树,那么如果所求的是$<=k种颜色的方案数 答案就是k * (k - 1)^ {n -1}$
$因为根节点有k种选择,其他结点都有k - 1种选择$
但实际上这包含了$k - 2, k - 3, k - 4 \cdots 2的方案数 容斥一下即可$
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 typedef long long ll; 6 7 const int maxn = 2510; 8 const ll MOD = 1e9 + 7; 9 10 ll fac[maxn], invfac[maxn], inv[maxn]; 11 12 ll qpow(ll x,ll n) 13 { 14 ll res = 1; 15 while(n) 16 { 17 if(n & 1) res = res * x % MOD; 18 x = x * x % MOD; 19 n >>= 1; 20 } 21 return res; 22 } 23 24 void Init() 25 { 26 fac[0] = invfac[0] = inv[0] = 1; 27 fac[1] = invfac[1] = inv[1] = 1; 28 for(int i = 2; i < maxn; ++i) 29 { 30 fac[i] = fac[i - 1] * i % MOD; 31 inv[i] = inv[MOD % i] * (MOD - MOD / i) % MOD; 32 invfac[i] = invfac[i - 1] * inv[i] % MOD; 33 } 34 } 35 36 ll calc(int n, int m) 37 { 38 if (n < 0 || m < 0) return 0; 39 if (n == m || m == 0) return 1; 40 return fac[n] * invfac[m] % MOD * invfac[n - m] % MOD; 41 } 42 43 int n, k; 44 45 int main() 46 { 47 Init(); 48 while(~scanf("%d %d",&n, &k)) 49 { 50 for(int i = 1, num; i < n; ++i) scanf("%d", &num); 51 int flag = 1; 52 ll ans = 0; 53 for(int i = k; i >= 2; --i, flag = !flag) 54 { 55 if(flag) ans = (ans + calc(k, i) * i % MOD * qpow(i - 1, n - 1) % MOD) % MOD; 56 else ans = (ans - calc(k, i) * i % MOD * qpow(i - 1, n - 1) % MOD + MOD) % MOD; 57 } 58 printf("%lld\n", ans); 59 } 60 return 0; 61 }