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 }
View Code

 

 

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 }
View Code

 

 

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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

 

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 }
View Code

 

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 }
View Code

 

 

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 }
View Code

 

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 }
View Code
posted @ 2018-12-08 18:43  Dup4  阅读(531)  评论(0编辑  收藏  举报