「美团 CodeM 初赛 Round A」试题泛做

最长树链

树形DP。我们发现gcd是多少其实并不重要,只要不是1就好了,此外只要有一个公共的质数就好了。计f[i][j]表示i子树内含有j因子的最长链是多少。因为一个数的不同的质因子个数是log级别的,所以转移的复杂度是log方的。所以总共的时间复杂度就是nlog^2n。

 1 #include <cstdio>
 2 #include <vector>
 3 #include <map>
 4 
 5 #define R register
 6 #define maxn 100010
 7 #define cmax(_a, _b) (_a < (_b) ? _a = (_b) : 0)
 8 bool vis[maxn];
 9 int pr[maxn / 10], prcnt, v[maxn];
10 struct Edge {
11     Edge *next;
12     int to;
13 } *last[maxn], e[maxn << 1], *ecnt = e;
14 inline void link(R int a, R int b)
15 {
16     *++ecnt = (Edge) {last[a], b}; last[a] = ecnt;
17     *++ecnt = (Edge) {last[b], a}; last[b] = ecnt;
18 }
19 int p[maxn][30], pc[maxn];
20 int f[maxn][30];
21 int ans;
22 void dfs(R int x, R int fa)
23 {
24     for (R Edge *iter = last[x]; iter; iter = iter -> next)
25         if (iter -> to != fa)
26         {
27             dfs(iter -> to, x);
28             for (R int i = 1; i <= pc[x]; ++i)
29             {
30                 R int nx = p[x][i];
31                 for (R int j = 1; j <= pc[iter -> to]; ++j)
32                     if (p[iter -> to][j] == nx)
33                     {
34                         cmax(ans, f[x][i] + f[iter -> to][j] + 1);
35                         cmax(f[x][i], f[iter -> to][j]);
36                     }
37             }
38         }
39     for (R int i = 1; i <= pc[x]; ++i) ++f[x][i];
40 }
41 int main()
42 {
43     R int n; scanf("%d", &n);
44     for (R int i = 2; i < maxn; ++i)
45     {
46         if (!vis[i]) pr[++prcnt] = i;
47         for (R int j = 1; j <= prcnt && i * pr[j] < maxn; ++j)
48         {
49             vis[i * pr[j]] = 1;
50             if (i % pr[j] == 0) break;
51         }
52     }
53     for (R int i = 1; i < n; ++i)
54     {
55         R int a, b; scanf("%d%d", &a, &b);
56         link(a, b);
57     }
58     for (R int i = 1; i <= n; ++i)
59     {
60         scanf("%d", v + i);
61         R int temp = v[i];
62         for (R int j = 1; 1ll * pr[j] * pr[j] <= v[i] && temp != 1; ++j)
63             if (temp % pr[j] == 0)
64             {
65                 p[i][++pc[i]] = pr[j];
66                 while (temp % pr[j] == 0) temp /= pr[j];
67             }
68         temp != 1 ? p[i][++pc[i]] = temp : 0;
69     }
70     dfs(1, 0);
71     printf("%d
", ans);
72     return 0;
73 }
最长树链

二分图染色

校内训练zzx出给我们做的。相当于求两个匹配的集合,使得两个集合的交集为空集。先设f[i]表示左边i个顶点,右边i个顶点的匹配的方案数。这个可以递推来求。然后再容斥一下,枚举交集为S的方案数。然后我们发现之和S的大小有关,所以就可以只枚举集合大小,剩下的用组合数算一算。

 1 #include <cstdio>
 2 
 3 #define R register
 4 #define maxn 10000010
 5 const int mod = 1e9 + 7;
 6 int f[maxn], pw[maxn], inp[maxn];
 7 inline int qpow(R int base, R int power)
 8 {
 9     R int ret = 1;
10     for (; power; power >>= 1, base = 1ll * base * base % mod)
11         power & 1 ? ret = 1ll * ret * base % mod : 0;
12     return ret;
13 }
14 inline int C(R int n, R int m)
15 {
16     return 1ll * pw[n] * inp[n - m] % mod;
17 }
18 int main()
19 {
20     R int n; scanf("%d", &n);
21     f[0] = 1; f[1] = 2;
22     for (R int i = 2; i <= n; ++i)
23         f[i] = (2ll * f[i - 1] - 1ll * (i - 1) * (i - 1) % mod * f[i - 2] + 2ll * (i - 1) * f[i - 1]) % mod;
24     pw[0] = 1;
25     for (R int i = 1; i <= n; ++i) pw[i] = 1ll * pw[i - 1] * i % mod;
26     inp[n] = qpow(pw[n], mod - 2);
27     for (R int i = n; i; --i) inp[i - 1] = 1ll * inp[i] * i % mod;
28     R int ans = 0;
29     for (R int i = 0, p = 1; i <= n; ++i, p = -p)
30         (ans += (mod + 1ll * p * inp[i] * C(n, i) % mod * C(n, i) % mod * f[n - i] % mod * f[n - i] % mod) % mod) %= mod;
31     printf("%d
", ans);
32     return 0;
33 }
二分图染色

倒水

我一开始想了半天的二分,然后后来发现只要大力分类讨论三种情况就好了。。。T小于温度最小值的,T大于温度最大值的,T在中间的。

最后一种是不成立的(因为最终的温度不可能大于或小于T,又因为T不可能和不等于T的水混合成T,所以不成立)。第一种只要看能否混成最小值就好了,第三种看一下能否混成最大值,如果能的话就把所有的水都用掉。

 1 #include <cstdio>
 2 
 3 #define R register
 4 #define maxn 100010
 5 #define cmin(_a, _b) (_a > (_b) ? _a = (_b) : 0)
 6 #define cmax(_a, _b) (_a < (_b) ? _a = (_b) : 0)
 7 typedef double db;
 8 int n, T, C, t[maxn], c[maxn];
 9 db left;
10 inline bool check(R db x)
11 {
12     left = C;
13     for (R int i = 1; i <= n; ++i)
14         if (t[i] != x)
15         {
16             if (T == x) return 0;
17             left -= (x * c[i] - (db) t[i] * c[i]) / (T - x);
18         }
19     return left >= 0;
20 }
21 int main()
22 {
23     scanf("%d%d%d", &n, &T, &C);
24     R int maxx = 0, minn = 0x7fffffff;
25     for (R int i = 1; i <= n; ++i) scanf("%d%d", t + i, c + i), cmax(maxx, t[i]), cmin(minn, t[i]);
26     if (T <= minn)
27     {
28         if (check(minn)) printf("Possible
%.4lf
", (db) minn);
29         else puts("Impossible");
30     }
31     else if (T >= maxx)
32     {
33         if (!check(maxx)) puts("Impossible");
34         else
35         {
36             R db p1 = (db) T * C, p2 = C;
37             for (R int i = 1; i <= n; ++i) p1 += (db) t[i] * c[i], p2 += c[i];
38             printf("Possible
%.4lf
", p1 / p2);
39         }
40     }
41     else puts("Impossible");
42     return 0;
43 }
倒水

身体训练

一开始题目看错傻逼了半天QAQ。。。设f[i][j]为第i个人排在第j位所需要的时间,我们发现这种情况出现的概率恰好是n!/(n-1)!=1/n次。所以直接乘上去加起来就好了。

 1 #include <cstdio>
 2 
 3 #define R register
 4 #define maxn 1010
 5 typedef double db;
 6 db c[maxn], d[maxn];
 7 int main()
 8 {
 9     R int n; R db v, u; scanf("%d%lf%lf", &n, &v, &u);
10     for (R int i = 1; i <= n; ++i) scanf("%lf", c + i);
11     for (R int i = 1; i <= n; ++i) scanf("%lf", d + i);
12     R db ans = 0;
13     for (R int i = 1; i <= n; ++i)
14     {
15         for (R int j = 0; j < n; ++j)
16             ans += u / (c[i] - d[i] * j - v);
17     }
18     printf("%.3lf
", ans);
19     return 0;
20 }
身体训练

合并回文子串

又是zzx校内训练出给我们做的。我们发现最终序列的一个子串在原本的两个序列上对应的是两个区间。所以我们计f[l1][r1][l2][r2]为用A[l1..r1],B[l2..r2]的字符能否组成一个回文串。然后转移的时候只要在端点转移进来。然后这个DP可以用bitset优化,然而我没去写。。。

 1 #include <cstdio>
 2 #include <cstring>
 3 
 4 #define R register
 5 #define maxn 60
 6 #define cmax(_a, _b) (_a < (_b) ? _a = (_b) : 0)
 7 bool f[maxn][maxn][maxn][maxn];
 8 char A[maxn], B[maxn];
 9 int main()
10 {
11     R int T; scanf("%d", &T);
12     for (; T; --T)
13     {
14         scanf("%s%s", A + 1, B + 1);
15         R int n = strlen(A + 1), m = strlen(B + 1);
16         memset(f, 0, sizeof (f));
17         for (R int i = 1; i <= n + 1; ++i)
18             for (R int j = 1; j <= m + 1; ++j)
19                 f[i][i - 1][j][j - 1] = f[i][i][j][j - 1] = f[i][i - 1][j][j] = 1;
20         for (R int ln = 0; ln <= n; ++ln)
21             for (R int l1 = 1, r1 = ln; r1 <= n; ++l1, ++r1)
22                 for (R int lm = 0; lm <= m; ++lm)
23                     for (R int l2 = 1, r2 = lm; r2 <= m; ++l2, ++r2)
24                     {
25                         f[l1][r1][l2][r2] |= f[l1 + 1][r1][l2][r2 - 1] && A[l1] == B[r2];
26                         f[l1][r1][l2][r2] |= f[l1][r1 - 1][l2 + 1][r2] && A[r1] == B[l2];
27                         if (ln > 1) f[l1][r1][l2][r2] |= A[l1] == A[r1] && f[l1 + 1][r1 - 1][l2][r2];
28                         if (lm > 1) f[l1][r1][l2][r2] |= B[l2] == B[r2] && f[l1][r1][l2 + 1][r2 - 1];
29                     }
30         R int ans = 0;
31         for (R int i = 1; i <= n; ++i)
32             for (R int j = i; j <= n; ++j)
33                 for (R int ii = 1; ii <= m; ++ii)
34                     for (R int jj = ii; jj <= m; ++jj)
35                         f[i][j][ii][jj] ? cmax(ans, j - i + 1 + jj - ii + 1) : 0;
36         printf("%d
", ans);
37     }
38     return 0;
39 }
合并回文子串

数列互质

我会分块莫队(°∀°)ノ!我们注意到一个性质:出现次数超过sqrt(n)的数不会超过sqrt(n)个。我们把这些数成为关键的数,并且可以预处理出它出现次数的前缀和。对于剩下的数出现次数就不会超过sqrt(n)了。我们用莫队把区间内所有非关键数的出现次数处理出来,并且可以处理出 出现次数为i的数的出现次数(好绕口)。。。对于每一个询问的k,先枚举关键的数,看一下出现次数是否满足条件。再枚举一下非关键数里面满足条件的数的个数。时间复杂度q*sqrt(n)*log,那个log是求gcd的。。。一开始直接求gcd被卡T了。后来预处理一下不知怎地就过了。╮( ̄▽ ̄)╭

 1 #include <cstdio>
 2 #include <cmath>
 3 #include <algorithm>
 4 #include <cstring>
 5 
 6 #define R register
 7 #define maxn 50010
 8 #define maxs 233
 9 int sum[maxs][maxn], cnt[maxn], a[maxn], ans[maxn], idd[maxn], ccnt[maxs], ip[maxs], gg[1010][1010];
10 struct Query {
11     int l, r, k, id;
12     inline bool operator < (const Query &that) const {return idd[l] < idd[that.l] || (idd[l] == idd[that.l] && (idd[l] & 1 ? r > that.r : r < that.r));}
13 } q[maxn];
14 bool imp[maxn];
15 int gcd(R int a, R int b)
16 {
17     return !b ? a : gcd(b, a % b);
18 }
19 int ggcd(R int a, R int b)
20 {
21     if (!a || !b) return a + b;
22     if (a <= 1000 && b <= 1000) return gg[a][b];
23     return ggcd(b, a % b);
24 }
25 inline void add(R int x)
26 {
27     if (imp[x]) return ;
28     cnt[x] ? --ccnt[cnt[x]] : 0;
29     ++ccnt[++cnt[x]];
30 }
31 inline void del(R int x)
32 {
33     if (imp[x]) return ;
34     --ccnt[cnt[x]--];
35     cnt[x] ? ++ccnt[cnt[x]] : 0;
36 }
37 int main()
38 {
39     R int n, m, num = 0, sq; scanf("%d%d", &n, &m); sq = (int) sqrt(n); sq < 5 ? sq = 5 : 0;
40     for (R int i = 1; i <= n; ++i) scanf("%d", a + i), ++cnt[a[i]], idd[i] = i / sq;
41     for (R int i = 1; i <= n; ++i)
42     {
43         if (cnt[i] >= sq)
44         {
45             ip[++num] = i; imp[i] = 1;
46 //            printf("i %d
", i);
47             for (R int j = 1; j <= n; ++j)
48                 sum[num][j] = sum[num][j - 1] + (a[j] == i);
49         }
50     }
51     memset(cnt, 0, sizeof (cnt));
52     for (R int i = 1; i <= m; ++i) scanf("%d%d%d", &q[i].l, &q[i].r, &q[i].k), q[i].id = i;
53     for (R int i = 1; i <= 1000; ++i)
54         for (R int j = 1; j <= 1000; ++j)
55             gg[i][j] = gcd(i, j);
56     std::sort(q + 1, q + m + 1);
57     R int l = 1, r = 0;
58     for (R int i = 1; i <= m; ++i)
59     {
60         while (q[i].r > r) add(a[++r]);
61         while (q[i].l < l) add(a[--l]);
62         while (q[i].l > l) del(a[l++]);
63         while (q[i].r < r) del(a[r--]);
64         for (R int j = 1; j <= sq; ++j)
65             if (ggcd(j, q[i].k) == 1) ans[q[i].id] += ccnt[j];
66         for (R int j = 1; j <= num; ++j)
67             if (sum[j][q[i].r] - sum[j][q[i].l - 1] > 0 && ggcd(sum[j][q[i].r] - sum[j][q[i].l - 1], q[i].k) == 1) ++ans[q[i].id];
68     }
69     for (R int i = 1; i <= m; ++i) printf("%d
", ans[i]);
70     return 0;
71 }
数列互质
原文地址:https://www.cnblogs.com/cocottt/p/7101972.html