Goodbye 2017 Solution

Problem A New Year and Counting Cards

題目大意

  (略)

  題目怎麼說就怎麼做。

Code

 1 /**
 2  * Codeforces
 3  * Problem#908A
 4  * Accepted
 5  * Time: 31ms
 6  * Memory: 0k
 7  */
 8 #include <bits/stdc++.h>
 9 using namespace std;
10 typedef bool boolean;
11 
12 int ans = 0;
13 char str[55];
14 boolean need[256];
15 
16 inline void init() {
17     scanf("%s", str);
18 }
19 
20 inline void solve() {
21     need['a'] = need['e'] = need['i'] = need['u'] = need['o'] = true;
22     need['1'] = need['3'] = need['5'] = need['7'] = need['9'] = true;
23     for (char *p = str; *p; p++)
24         ans += need[*p];
25     printf("%d\n", ans);
26 }
27 
28 int main() {
29     init();
30     solve();
31     return 0;
32 }
Problem A

Problem B New Year and Buggy Bot

題目大意

  (略)

  暴力枚舉映射,然後模擬check。

Code

 1 /**
 2  * Codeforces
 3  * Problem#908B
 4  * Accepted
 5  * Time: 31ms
 6  * Memory: 0k
 7  */
 8 #include <bits/stdc++.h>
 9 using namespace std;
10 typedef bool boolean;
11 
12 const int mov[2][4] = {{0, 1, 0, -1}, {1, 0, -1, 0}};
13 
14 int n, m, l;
15 int sx, sy;
16 int key[4];
17 char str[105];
18 char a[55][55];
19 
20 inline void init() {
21     scanf("%d%d", &n, &m);
22     for (int i = 1; i <= n; i++) {
23         scanf("%s", a[i] + 1);
24         for (int j = 1; j <= m; j++) {
25             if (a[i][j] == 'S') {
26                 sx = i, sy = j;
27             }
28         }
29     }
30     scanf("%s", str);
31     l = strlen(str);
32     for (int i = 0; i < l; i++)
33         str[i] -= '0';
34 }
35 
36 int ans = 0;
37 boolean simulate() {
38     int curx = sx, cury = sy;
39     for (int i = 0; i < l; i++) {
40         int dir = key[str[i]];
41         curx += mov[0][dir], cury += mov[1][dir];
42         if (curx < 1 || curx > n || cury < 1 || cury > m)
43             return false;
44         if (a[curx][cury] == '#')
45             return false;
46         if (a[curx][cury] == 'E')
47             return true;
48     }
49     return false;
50 }
51 
52 inline void solve() {
53     for (int a = 0; a < 4; a++)
54         for (int b = 0; b < 4; b++)
55             if (a != b)
56                 for (int c = 0; c < 4; c++)
57                     if (c != b && c != a)
58                         for (int d = 0; d < 4; d++)
59                             if ((a != d) && (b != d) && (c != d)) {
60                                 key[0] = a, key[1] = b;
61                                 key[2] = c, key[3] = d;
62                                 ans += simulate();
63                             }
64     printf("%d\n", ans);
65 }
66 
67 int main() {
68     init();
69     solve();
70     return 0;
71 }
Problem B

Problem C New Year and Curling

題目大意

  有$n$個半徑爲$r$的圓,依次將第$i$個圓的圓心放在$x = x_i$的無限遠處,然後向$y = 0$滑動(橫座標不變),當圓接觸到$y = 0$或者接觸到之前的圓就會停止滑動。求出每個圓停止時圓心的縱座標。

  暴力找到每個圓與哪個圓最終接觸,或者接觸到$y = 0$。然後用小學幾何知識計算圓心的縱座標。

Code

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

Problem D New Year and Arbitrary Arrangement

題目大意

  有一個空字符串$s$,每次有$p_a$的概率向$s$的末尾添加一個字符a,有$p_b$的概率向$s$的末尾添加一個字符b。當$s$中子序列ab的個數不少於$k$的時候停止。問停止時子序列ab的個數的期望。

  暴力dp是用$f_{i, j}$表示當前有$i$個子序列,其中已經有$j$個a的概率。

  但是$j$可以無限大。不難注意到當$a \geqslant k$的時候只要出現b就會停止,這一部分可以用等比數列求和的方法直接計算。

  因此我們只用暴力處理$j < k$的情況。

Code

 1 /**
 2  * Codeforces
 3  * Problem#908D
 4  * Accepted
 5  * Time: 31ms
 6  * Memory: 4100k
 7  */
 8 #include <bits/stdc++.h>
 9 using namespace std;
10 typedef bool boolean;
11 
12 const int Mod = 1e9 + 7;
13 
14 int add(int a, int b) {
15     return ((a += b) >= Mod) ? (a - Mod) : (a);
16 }
17 
18 int mul(int a, int b) {
19     return a * 1ll * b % Mod;
20 }
21 
22 void exgcd(int a, int b, int& x, int& y) {
23     if (!b)
24         x = 1, y = 0;
25     else {
26         exgcd(b, a % b, y, x);
27         y -= (a / b) * x;
28     }
29 } 
30 
31 int inv(int a) {
32     int x, y;
33     exgcd(a, Mod, x, y);
34     return (x < 0) ? (x + Mod) : (x);
35 }
36 
37 const int N = 1024;
38 
39 int k, pa, pb;
40 int f[N][N];
41 
42 inline void init() {
43     scanf("%d%d%d", &k, &pa, &pb);
44     pa = mul(pa, inv(pa + pb));
45     pb = Mod + 1 - pa;
46 }
47 
48 int ans = 0;
49 inline void solve() {
50     f[0][1] = 1;
51     int _pb = inv(pb);
52     for (int s = 0; s < k; s++) {
53         for (int cnt_a = 1; cnt_a < k; cnt_a++) {
54             int val = f[s][cnt_a];
55             if (s + cnt_a < k) {
56                 f[s + cnt_a][cnt_a] = add(f[s + cnt_a][cnt_a], mul(val, pb));
57             } else {
58                 ans = add(ans, mul(mul(val, pb), s + cnt_a));    
59             }
60             f[s][cnt_a + 1] = add(f[s][cnt_a + 1], mul(val, pa));
61         }
62         int prob = mul(f[s][k], pb);
63         int E = mul(k + s, _pb);
64         E = add(E, mul(pa, mul(_pb, _pb)));
65         ans = add(ans, mul(E, prob));
66     }
67     printf("%d\n", ans);
68 }
69 
70 int main() {
71     init();
72     solve();
73     return 0;
74 }
Problem D

Problem E New Year and Entity Enumeration

題目大意

  (原題題面很簡潔)

  設$f_i$表示集合$S$中第$i$位爲1的數的and和。

  對於$j \neq i$,那麼要麼$f_i = f_j$要麼$f_i \cap f_j = \varnothing$。

  設$b = f_i \cap f_j$,若$f_i \neq f_j$,並且$b\neq \varnothing$

  • 如果$i,j \in b$,容易推出$f_i = f_j$,矛盾。
  • 如果$i\in b, j\not \in b$,那麼必然存在一個數第$i$位爲1,第$j$爲0,使得$j\not \in f_i$,考慮這個數的補,由此推出$i \not \in b$,矛盾。
  • 如果$i\not \in b, j \in b$,同理可得。
  • 如果$i\not \in b, j \not \in b$,那麼存在一個$x\in b$,所以存在一個數$y$使得在第$i$位爲1,第$x$位爲1, 第$j$位爲0,取它的補,那麼就有第$i$位爲0,第$x$位爲0,第$j$位爲1,由此推出$x\not \in b$,矛盾。

  那麼$f_i$可以看成對這幾個二進制位的一個劃分,對於$j\in f_i$的二進制位劃在同一個集合。

  可以證明任意一個劃分和一個集合是一一對應的。

  也可以證明對於$S$的劃分一定是由$T$的一個劃分中每個集合繼續劃分得到的。

  然而我都不會證明

  這個集合劃分的方案數Bell數,每一部分獨立,乘起來就好了。設$s_i$表示第$i$個劃分出來的集合的大小,那麼

$ans = \prod_{i = 1} B_{s_i}$

Code

 1 /**
 2  * Codeforces
 3  * Problem#908E
 4  * Accepted
 5  * Time: 31ms
 6  * Memory: 4000k
 7  */
 8 #include <bits/stdc++.h>
 9 using namespace std;
10 typedef bool boolean;
11 
12 const int Mod = 1e9 + 7;
13 const int N = 1005, M = 55;
14 
15 int add(int a, int b) {
16     return ((a += b) >= Mod) ? (a - Mod) : (a); 
17 }
18 
19 int mul(int a, int b) {
20     return a * 1ll * b % Mod;
21 }
22 
23 int n, m;
24 char str[N];
25 boolean vis[N];
26 bitset<N> a[M], f, all;
27 
28 inline void init() {
29     scanf("%d%d", &n, &m);
30     for (int i = 0; i < m; i++) {
31         scanf("%s", str);
32         for (int j = 0; j < n; j++)
33             if (str[j] == '1')
34                 a[i].set(j);
35     }
36 }
37 
38 int bell[N];
39 int comb[N][N];
40 
41 inline void solve() {
42     comb[0][0] = 1;
43     for (int i = 1; i <= n; i++) {
44         comb[i][0] = comb[i][i] = 1;
45         for (int j = 1; j < i; j++) {
46             comb[i][j] = add(comb[i - 1][j - 1], comb[i - 1][j]);
47         }
48     }
49     bell[0] = 1;
50     for (int i = 1; i <= n; i++) {
51         for (int j = 1; j <= i; j++) {
52             bell[i] = add(bell[i], mul(comb[i - 1][j - 1], bell[i - j]));
53         }
54     }
55     for (int i = 0; i < n; i++)
56         all.set(i);
57     int ans = 1;
58     for (int i = 0; i < n; i++) {
59         if (vis[i])
60             continue;
61         f.set();
62         for (int j = 0; j < m; j++) {
63             if (a[j].test(i)) {
64                 f &= a[j];
65             } else {
66                 f &= (a[j] ^ all);
67             }
68         }
69         int cnt = 0;
70         for (int j = 0; j < n; j++) {
71             if (f.test(j)) {
72                 vis[j] = true;
73                 cnt++;
74             }
75         }
76         ans = mul(ans, bell[cnt]);
77     }
78     printf("%d\n", ans);
79 }
80 
81 int main() {
82     init();
83     solve();
84     return 0;
85 }
Problem E

Problem F New Year and Rainbow Roads

題目大意

  有三種顏色的點在數軸上,每個點的顏色是紅藍綠中的一種,連接兩個點的代價是兩個點的距離。要求刪掉所有紅點或者藍點後剩下的點依然連通。問最小代價。

  開始讀錯題,以爲還要滿足是棵樹。

  有一些比較顯然的結論:

  • 紅點和藍點間連邊不優,因爲它根本不會有用。
  • 綠點和綠點間,綠點和邊界的部分是獨立的。(連邊跨過綠點可以變成連向綠點)。

  然後紅點和藍點獨立了。

  綠點和邊界之間連法只有一種。比較顯然。

  考慮綠點和綠點間的連邊。

  • 綠點和綠點有邊,那麼斷開中間兩個相鄰的距離最遠的同色點之間的邊。
  • 綠點和綠點之間沒有邊,那麼只能每種顏色連成一條鏈。

  如果沒有綠點,那麼紅點和藍點各自連成一條鏈。

Code

  1 /**
  2  * Codeforces
  3  * Problem#908F
  4  * Accepted
  5  * Time: 93ms
  6  * Memory: 2400k
  7  */
  8 #include <bits/stdc++.h>
  9 using namespace std;
 10 typedef bool boolean;
 11 
 12 #define ll long long
 13 
 14 const signed inf = (signed) (~0u >> 1);
 15 const int N = 3e5 + 5;
 16 
 17 int n;
 18 char str[5];
 19 int p[N], col[N];
 20 
 21 inline void init() {
 22     scanf("%d", &n);
 23     for (int i = 1; i <= n; i++) {
 24         scanf("%d%s", p + i, str);
 25         if (str[0] == 'R')
 26             col[i] = 1;
 27         if (str[0] == 'G')
 28             col[i] = 2;
 29     }
 30 }
 31 
 32 ll ans = 0;
 33 void calc(int l, int r) {
 34     ll delta = (p[r] - p[l]) * 3ll;
 35     int reduce1 = 0, reduce2 = 0;
 36     int lst_b = p[l], lst_r = p[l];
 37     for (int i = l + 1; i < r; i++) {
 38         if (col[i] == 1) {
 39             reduce2 = max(reduce2, p[i] - lst_r);
 40             lst_r = p[i];
 41         } else {
 42             reduce1 = max(reduce1, p[i] - lst_b);
 43             lst_b = p[i];
 44         }
 45     }
 46     reduce1 = max(reduce1, p[r] - lst_b);
 47     reduce2 = max(reduce2, p[r] - lst_r);
 48     delta -= reduce1;
 49     delta -= reduce2;
 50     delta = min(delta, (ll) (p[r] - p[l]) << 1);
 51     ans += delta;
 52 }
 53 
 54 inline void solve() {
 55     int lst = -1;
 56     int first_b = -1, first_r = -1;
 57     for (int i = 1; i <= n; i++) {
 58         if (col[i] == 2) {
 59             if (lst == -1) {
 60                 if (first_b != -1)
 61                     ans += p[i] - first_b;
 62                 if (first_r != -1)
 63                     ans += p[i] - first_r;
 64             } else {
 65                 calc(lst, i);
 66             }
 67             lst = i;
 68         } else {
 69             if (first_b == -1 && !col[i])
 70                 first_b = p[i];
 71             if (first_r == -1 && col[i] == 1)
 72                 first_r = p[i];
 73         }
 74     }
 75     if (lst == -1) {
 76         int bmi = inf, bmx = -1;
 77         int rmi = inf, rmx = -1;
 78         for (int i = 1; i <= n; i++) {
 79             if (col[i] == 0) {
 80                 bmi = min(bmi, p[i]);
 81                 bmx = max(bmx, p[i]);
 82             } else {
 83                 rmi = min(rmi, p[i]);
 84                 rmx = max(rmx, p[i]);
 85             }
 86         }
 87         if (~bmx)
 88             ans += bmx - bmi;    
 89         if (~rmx) 
 90             ans += rmx - rmi;
 91     } else {
 92         int last_b = -1, last_r = -1;
 93         for (int i = n; i; i--) {
 94             if (col[i] == 2) {
 95                 if (~last_b)
 96                     ans += last_b - p[i];
 97                 if (~last_r)
 98                     ans += last_r - p[i];    
 99                 break;
100             }
101             
102             if (last_b == -1 && col[i] == 0)
103                 last_b = p[i];
104             if (last_r == -1 && col[i] == 1)
105                 last_r = p[i];
106         }
107     }
108     printf("%I64d", ans);
109 } 
110 
111 int main() {
112     init();
113     solve();
114     return 0;
115 }
Problem F

Problem G New Year and Original Order

題目大意

  定義一個數的權值是將它的所有數字從大到小排序後得到的數,給定$X$,問不超過$X$的所有正整數的權值和。

  枚舉位數,計算排序後,這一位大於等於$k$的數的個數。

Code

 1 /**
 2  * Codeforces
 3  * Problem#908G
 4  * Accepted
 5  * Time: 499ms
 6  * Memory: 39000k
 7  */
 8 #include <bits/stdc++.h>
 9 using namespace std;
10 typedef bool boolean;
11 
12 const int N = 705, Mod = 1e9 + 7;
13 
14 int add(int a, int b) {
15     return ((a += b) >= Mod) ? (a - Mod) : (a);
16 }
17 
18 int mul(int a, int b) {
19     return a * 1ll * b % Mod;
20 }
21 
22 int n;
23 int ans = 0;
24 char str[N];
25 int f[N][10][N][2];
26 
27 inline void init() {
28     scanf("%s", str + 1);
29     n = strlen(str + 1);
30     for (int i = 1; i <= n; i++)
31         str[i] -= '0';
32 }
33 
34 int dp(int bit, int num, int rest, boolean onlim) {
35     if (!bit)
36         return !rest;
37     int& rt = f[bit][num][rest][onlim];
38     if (~rt)
39         return rt;
40     rt = 0;
41     int lim = (onlim) ? (str[n - bit + 1]) : (9);
42     for (int i = 0; i <= lim; i++) {
43         boolean nx_onlim = onlim && i == lim;
44         rt = add(rt, dp(bit - 1, num, max(rest - (i >= num), 0), nx_onlim));
45     }
46     return rt;
47 }
48 
49 inline void solve() {
50     memset(f, -1, sizeof(f));
51     int base = 1;
52     for (int bit = 1; bit <= n; bit++, base = mul(base, 10)) {
53         for (int num = 1, tmp = base; num < 10; num++, tmp = add(tmp, base)) {
54 //            int g = dp(n, num, bit, true, false);
55             ans = add(ans, mul(dp(n, num, bit, true), base));
56 //            cerr << bit << " " << num << " " << g << '\n';
57         }
58     }
59     printf("%d\n", ans);
60 }
61 
62 int main() {
63     init();
64     solve();
65     return 0;
66 }
Problem G

Problem H New Year and Boolean Bridges

題目大意

  有一個有向圖,對於兩個點$(u, v)$,已知任意兩點滿足下列條件之一

  • $u$能到達$v$並且$v$能到達$u$
  • $u$能到達$v$或者$v$能到達$u$
  • $u$能到達$v$和$v$能到達$u$恰好有一條成立

  問滿足條件的圖中,最少的邊數。無解輸出-1.

  $and$關係是強連通,可以先用並查集把這一部分先縮起來。如果有兩個在同一強連通分量內的點有$XOR$的關係,那麼答案是$-1$。

  對於如果一個強連通分量的大小爲$x$,那麼當$x = 1$時,它內部的最少邊數爲$0$,否則爲$1$。 

  對於剩下的點,一種合法的方案是是排成一行,連成一條鏈。

  這個還可以怎麼優化?考慮對於一些或關係,我把它變成強連通關係,如果這兩個強連通分量的大小都大於1,那麼我可以省去一條邊。

  因此大小爲1的強連通分量對答案的貢獻總是$1$(當只存在大小爲1的強連通分量時需要特判)。

  考慮剩下的連通塊,它至多有$23$個。

  首先可以預處理出$f_{0,s}$,表示$s$中的點能不能連成一個強連通分量。

  於是我們可以得到一個優秀的$O(3^{\left \lfloor \frac{n}{2} \right \rfloor})$的暴力dp。

  設$f_{k, s}$表示添加$k$條邊,能否是$s$中的點連成一條鏈。

  對於$f_{k, s}$能否爲真,等價於判斷$\sum_{a\cup b = s} f_{k - 1, a}f_{0, b}$是否非0.

  因此$f_{k}$由$f_{k - 1}$和$f_0$做或卷積得到。

  每次卷積完用容斥解一下$f_{k, U}$。

Code

  1 /**
  2  * Codeforces
  3  * Problem#908H
  4  * Accepted
  5  * Time: 592ms
  6  * Memory: 98500k
  7  */
  8 #include <bits/stdc++.h>
  9 using namespace std;
 10 typedef bool boolean;
 11 
 12 template <typename T>
 13 void pcopy(T* pst, const T* ped, T* pval) {
 14     for ( ; pst != ped; *(pst++) = *(pval++));
 15 }
 16 
 17 const int N = 50, S = 1 << 23;
 18 
 19 int n;
 20 int ans;
 21 int a[N][N]; // 0 : or, 1 : xor, 2 : and, 3 : nothing
 22 int G[24][24];    // after union
 23 int uf[N], sz[N];
 24 int f[S], g[S], bit[S];
 25 
 26 int find(int x) {
 27     return (uf[x] == x) ? (x) : (uf[x] = find(uf[x]));
 28 }
 29 
 30 boolean have_and = false;
 31 inline void init() {
 32     static char str[N + 4];
 33     scanf("%d", &n);
 34     for (int i = 0; i < n; i++)
 35         uf[i] = i;
 36     for (int i = 0; i < n; i++) {
 37         scanf("%s", str);
 38         for (int j = 0; j < n; j++) {
 39             if (i == j) {
 40                 a[i][j] = 3;
 41             } else if (str[j] == 'A') {
 42                 a[i][j] = 2;
 43                 have_and = true;
 44                 uf[find(i)] = find(j);
 45             } else if (str[j] == 'X') {
 46                 a[i][j] = 1;
 47             }
 48         }
 49     }
 50 }
 51 
 52 void fwt(int* f, int n) {
 53     for (int i = 1; i < n; i <<= 1) {
 54         for (int j = 0; j < n; j++) {
 55             if (j & i) {
 56                 f[j] += f[j ^ i];
 57             }
 58         }
 59     }
 60 }
 61 
 62 inline int discrete() {
 63     static int id[N];
 64     vector<int> val;
 65     for (int i = 0; i < n; i++) {
 66         sz[find(i)]++;
 67     }
 68     for (int i = 0; i < n; i++) {
 69         if (find(i) == i && sz[i] > 1) {
 70             val.push_back(i);
 71         }
 72     }
 73     for (int i = 0; i < n; i++) {
 74         id[i] = lower_bound(val.begin(), val.end(), find(i)) - val.begin();
 75     }
 76     for (int i = 0; i < n; i++) {
 77         for (int j = 0; j < n; j++) {
 78             int u = find(i), v = find(j);
 79             if (sz[u] == 1 || sz[v] == 1) {
 80                 continue;
 81             }
 82             if (u ^ v) {
 83                 G[id[u]][id[v]] |= a[i][j];
 84             }
 85         }
 86     }
 87     return val.size();
 88 }
 89 
 90 #define sgn(__) (((__) & 1) ? (-1) : (1))
 91 
 92 inline void solve() {
 93     if (!have_and) {
 94         printf("%d\n", n - 1);
 95         return;
 96     }
 97     for (int i = 0; i < n; i++) {
 98         for (int j = i + 1; j < n; j++) {
 99             if (a[i][j] == 1 && find(i) == find(j)) {
100                 puts("-1");
101                 return;
102             }
103         }
104     }
105     ans = n;
106     n = discrete();
107     f[0] = 1;
108     for (int i = 1; i < (1 << n); i++) {
109         int x = -1;
110         for (int j = 0; j < n; j++) {
111             if (i & (1 << j)) {
112                 x = j;
113                 break;
114             }
115         }
116         assert(~x);
117         if (!f[i ^ (1 << x)])
118             continue;
119         f[i] = 1;
120         for (int j = x + 1; j < n && f[i]; j++) {
121             if (i & (1 << j)) {
122                 f[i] &= !G[x][j];    
123             }
124         }
125     }
126     pcopy(g, g + (1 << n), f);
127     int all = (1 << n) - 1;
128     bit[0] = 0;
129     for (int s = 1; s <= all; s++) {
130         bit[s] = bit[s >> 1] + (s & 1);
131     }
132     fwt(f, all + 1);
133     fwt(g, all + 1);
134     for (int cnt = 0; cnt <= n; cnt++, ans++) {
135         int fn = 0;
136         for (int j = 0; j <= all; j++) {
137             fn += g[j] * sgn(n - bit[j]);
138         }
139         if (fn) {
140             break;
141         }
142         for (int j = 0; j <= all; j++)
143             g[j] = g[j] * f[j];
144     }
145     printf("%d\n", ans);
146 }
147 
148 int main() {
149     init();
150     solve();
151     return 0;
152 }
Problem H
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章