码迷,mamicode.com
首页 > 数据库 > 详细

Goodbye 2017 Solution

时间:2019-01-07 22:33:41      阅读:199      评论:0      收藏:0      [点我收藏+]

标签:put   min   add   top   int   signed   rds   cer   独立   

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

Goodbye 2017 Solution

标签:put   min   add   top   int   signed   rds   cer   独立   

原文地址:https://www.cnblogs.com/yyf0309/p/10222956.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!