codeforces Educational Round 80


A、Deadline

题意:

完成一个计划需要$d$天,但是可以优化,优化$x$天的情况下,完成时间是$x+\lceil \frac{d}{x+1} \rceil$天。求$n$天内能否完成?

题解:

枚举天数即可,显然只需要枚举到$sqrt(d)$,如果可行马上输出。直接计算也可以,但是博主不会

AC代码:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int N = 1e5 + 5;
 4 void solve()
 5 {
 6     int n, d;
 7     scanf("%d%d", &n, &d);
 8     for (int i = 0; i * i <= d; ++i)
 9     {
10         int ans = 0;
11         if (d % (i + 1))
12             ans = d / (i + 1) + 1 + i;
13         else
14             ans = d / (i + 1) + i;
15         if (n >= ans)
16         {
17             puts("YES");
18             return;
19         }
20     }
21     puts("NO");
22 }
23 int main()
24 {
25     int T;
26     scanf("%d", &T);
27     while (T--)
28         solve();
29     return 0;
30 }
View Code

B、Yet Another Meme Problem

题意:

数字$a$范围在$[1,A]$,$b$范围在$[1,B]$,将$a$和$b$拼接起来,求这个数字等于$a*b+a+b$的值时,$a$和$b$取值的对数。

题解:

我们令$b$的位数是$k$,则$con(a,b) = a*10^k+b$,然后化简得$b=10^k-1$时即可。所以就找符合的$b$即可,此时$a$任意。

AC代码:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int N = 1e5 + 5;
 5 void solve()
 6 {
 7     ll n, m;
 8     scanf("%lld%lld", &n, &m);
 9     ll cnt = 0;
10     for (ll i = 10; i - 1 <= m; i *= 10)
11         ++cnt;
12     printf("%lld\n", cnt * n);
13 }
14 int main()
15 {
16     int T;
17     scanf("%d", &T);
18     while (T--)
19         solve();
20     return 0;
21 }
View Code

*C、Two Arrays

题意:

给出$n$和$m$,求出长度都是$m$的序列$a$和$b$,使得对于所有的$1 \leq i \leq m$:$a_i \leq n, b_i \leq n, a_i \leq a_{i+1},b_i \geq b_{i+1},a_i \leq b_i$。

题解:

解法一、

令$dp[i][j]$表示第$i$个数的$b_i-a_i=j$的方案数,转移方程:

$dp[i][j] = \sum \limits _{k = j}^{n-1} dp[i-1][k]*(k-j+1)$

可以使用前缀和优化,但是不优化也能过。

AC代码:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int N = 1e3 + 5, M = 1e1 + 5;
 5 const ll mod = 1e9 + 7;
 6 ll dp[M][N], sum[N];
 7 void solve()
 8 {
 9     int n, m;
10     scanf("%d%d", &n, &m);
11     for (int i = 0; i < n; ++i)
12         dp[1][i] = n - i;
13     for (int i = 2; i <= m; ++i)
14         for (int j = 0; j < n; ++j)
15             for (int k = j; k < n; ++k)
16                 dp[i][j] = (dp[i][j] + dp[i - 1][k] * (k - j + 1)) % mod;
17     ll ans = 0;
18     for (int i = 0; i < n; ++i)
19         ans = (ans + dp[m][i]) % mod;
20     printf("%lld\n", ans);
21 }
22 int main()
23 {
24     int T = 1;
25     //scanf("%d", &T);
26     while (T--)
27         solve();
28     return 0;
29 }
View Code

解法二、

官方题解的做法,反正我是惊呆了。

考虑长度是$2*m$的序列$a_1,a_2,...,a_m,b_m,...b_2,b_1$,这个序列是单调不降的,等价于从$1$到$n$中选择$2*m$个数组成的可重集的数量的和。所以答案就是$C_{n+2*m-1}^{2*m}$

取模求个逆元就行。

AC代码:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int N = 1.1e3 + 5;
 5 const ll mod = 1e9 + 7;
 6 ll pow(ll a, ll b, ll p)
 7 {
 8     ll res = 1;
 9     while (b)
10     {
11         if (b & 1)
12             res = res * a % p;
13         a = a * a % p;
14         b >>= 1;
15     }
16     return res;
17 }
18 ll fac[N];
19 void solve()
20 {
21     fac[0] = 1;
22     for (int i = 1; i < N; ++i)
23         fac[i] = fac[i - 1] * i % mod;
24     int n, m;
25     scanf("%d%d", &n, &m);
26     printf("%lld\n", fac[n + 2 * m - 1] * pow(fac[n - 1], mod - 2, mod) % mod * pow(fac[2 * m], mod - 2, mod) % mod);
27 }
28 int main()
29 {
30     int T = 1;
31     //scanf("%d", &T);
32     while (T--)
33         solve();
34     return 0;
35 }
View Code

*D、Minimax Problem

题意:

给出$n$行$m$列的矩阵,$n \leq 3e5,m \leq 8$。求两行(可以一样),使得$min(max _{1\leq i \leq m} (a_i, b_i))$最大。

题解:

使最小值最大,必然先考虑二分答案。但是我们要怎么验证?注意到二分的答案的含义是:存在一个选法使得结果大于这个答案,那么取右半,否则取左半。如果$a_i$和$b_i$只要有一个大于这个答案,$max$就大于这个答案,所以考虑把大于等于设成$1$,否则设成$0$,然后状压。将每一列变成一个$[0.255]$的数,然后相当于是找两个数,使得$c_i | c_j = 2^m-1$,暴力找时间复杂度不对,考虑剪枝:如果一个数本身就是$2^m-1$,直接返回,如果一个数是$0$跳过。还可以建图寻找,但是实际上使用剪枝即可通过。注意!如果最终找不到大于$0$的答案,输出$1\ 1$。

AC代码:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int N = 3e5 + 5;
 4 int n, m, px, py;
 5 int a[N][15];
 6 int buc[305];
 7 bool check(int x)
 8 {
 9     memset(buc, 0, sizeof(buc));
10     for (int i = 1; i <= n; ++i)
11     {
12         int tmp = 0;
13         for (int j = 1; j <= m; ++j)
14             tmp |= (a[i][j] >= x) << (j - 1);
15         buc[tmp] = i;
16     }
17     for (int i = 0; i < (1 << m); ++i)
18         for (int j = 0; j < (1 << m); ++j)
19             if (buc[i] && buc[j] && (i | j) == (1 << m) - 1)
20             {
21                 px = buc[i], py = buc[j];
22                 return true;
23             }
24     return false;
25 }
26 void solve()
27 {
28     scanf("%d%d", &n, &m);
29     for (int i = 1; i <= n; ++i)
30         for (int j = 1; j <= m; ++j)
31             scanf("%d", &a[i][j]);
32     int l = 0, r = 1e9 + 5;
33     while (l < r)
34     {
35         int m = (l + r) >> 1;
36         if (!check(m))
37             r = m;
38         else
39             l = m + 1;
40     }
41     if (!l)
42         printf("1 1\n");
43     else
44         printf("%d %d\n", px, py);
45 }
46 int main()
47 {
48     int T = 1;
49     //scanf("%d", &T);
50     while (T--)
51         solve();
52     return 0;
53 }
View Code

E、Messenger Simulator

题意:

给出一个$n$个人的序列,编号$1~n$,和$m$次操作,每次操作可以让一个人到最前面,然后他前面的人往后退一个人,求这个过程中每个人最前和最后的位置。

题解:

这不是$splay$裸题吗

这个题目实际上就是查找树节点在第$k$大和插入到最左边。所以$splay$可做,但是太麻烦,没必要。

求第$k$大可以用树状数组或者权值线段树,然后考虑到这些数都是往最前面移,所以后面移动的一定在前面移动的前面,所以就建立一棵大小是$m+n$的树状数组,后$n$个数置为$1$,前$m$个数置为$0$,记录初始序列人的位置是$pos[a_i] = m+i$,然后第$i$次操作,就先统计一下当前需要操作的结点的第$k$大,然后移到$m-i+1$,更新$pos$数组,在所有操作完成后再更新一次所有位置即可。

AC代码:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int N = 3e5 + 5;
 4 struct BIT
 5 {
 6     int c[N << 1];
 7     void update(int pos, int n, int v)
 8     {
 9         for (; pos <= n; pos += pos & (-pos))
10             c[pos] += v;
11     }
12     int query(int pos)
13     {
14         int res = 0;
15         for (; pos; pos -= pos & (-pos))
16             res += c[pos];
17         return res;
18     }
19 };
20 BIT tr;
21 pair<int, int> ans[N];
22 int pos[N];
23 void solve()
24 {
25     memset(tr.c, 0, sizeof(tr.c));
26     int n, m, a;
27     scanf("%d%d", &n, &m);
28     for (int i = 1; i <= n; ++i)
29         ans[i] = {i, i}, pos[i] = m + i;
30     for (int i = m + 1; i <= m + n; ++i)
31         tr.update(i, m + n, 1);
32     for (int i = 1; i <= m; ++i)
33     {
34         scanf("%d", &a);
35         int p = pos[a];
36         ans[a].second = max(ans[a].second, tr.query(p));
37         ans[a].first = 1;
38         tr.update(p, m + n, -1);
39         pos[a] = m + 1 - i;
40         tr.update(pos[a], m + n, 1);
41     }
42     for (int i = 1; i <= n; ++i)
43         ans[i].second = max(ans[i].second, tr.query(pos[i]));
44     for (int i = 1; i <= n; ++i)
45         printf("%d %d\n", ans[i].first, ans[i].second);
46 }
47 int main()
48 {
49     int T = 1;
50     //scanf("%d", &T);
51     while (T--)
52         solve();
53     return 0;
54 }
View Code

 

posted @ 2020-07-04 00:30  Aya_Uchida  阅读(143)  评论(0编辑  收藏  举报