Codeforces Round #610 (Div. 2) 前5题题解

Codeforces Round #610 (Div. 2) 前5题题解

感觉这场比赛质量不错,出题人真的良心,样例数据给那么详细。

这次我还第一次遇到了ILE(Idleness limit exceeded),原来fflushu(stdout)是每次输出后都要用的……

比赛传送门

 

A.Temporarily unavailable

题目大意:有一条数轴,要从a点跑到b点,在c点的地方有网络,覆盖范围为半径为r的圆。问在多少时间有网路覆盖。

因为是在数轴上的,所以有网路覆盖的地方就是(c - r,c + r),那么只要减一下就好,注意可能有些区域在跑的地方的外面。

代码如下:

 

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <cstring>
 5 #include <vector>
 6 #define rep(x, l, r) for(int x = l; x <= r; x++)
 7 #define repd(x, r, l) for(int x = r; x >= l; x--)
 8 #define clr(x, y) memset(x, y, sizeof(x))
 9 #define all(x) x.begin(), x.end()
10 #define pb push_back
11 #define mp make_pair
12 #define MAXN
13 #define fi first
14 #define se second
15 #define SZ(x) ((int)x.size())
16 using namespace std;
17 typedef long long ll;
18 typedef vector<int> vi;
19 typedef pair<int, int> pii;
20 const int INF = 1 << 30;
21 const int p = 1000000009;
22 int lowbit(int x){ return x & (-x);}
23 int fast_power(int a, int b){ int x; for(x = 1; b; b >>= 1){ if(b & 1) x = 1ll * x * a % p; a = 1ll * a * a % p;} return x % p;}
24 
25 int main(){
26     int t;
27     scanf("%d", &t);
28     while(t--){
29         int a, b, c, r;
30         scanf("%d%d%d%d", &a, &b, &c, &r);
31         printf("%d\n", max(a, b) - min(a, b) - max(0, min(max(a, b), c + r) - max(min(a, b), c - r)));
32     }
33     return 0;
34 }
View Code

 

 

B1.K for the Price of One(Easy Version)

题目大意:有n个商品,每个商品价值为 ai ,现在有一个活动优惠,买一个物品可以选择k - 1个价值小于等于该它的物品免费获得(要么一个也不选,要么一定要选k - 1个),求k个硬币一共能买多少物品。在该题中k = 2。

不难发现,买下来的的物品一定是最便宜的那几个。我们设状态 dp[i][0/1] 表示第i个物品选还是不选的最小花费。

若是不选了i个,i - 1肯定要选,若是选了第i个,第i - 1个可以选可以不选,得到转移方程。

最后找到最大的 dp[i][1] 小于等于p即为答案。

代码如下:

 

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <cstring>
 5 #include <vector>
 6 #define rep(x, l, r) for(int x = l; x <= r; x++)
 7 #define repd(x, r, l) for(int x = r; x >= l; x--)
 8 #define clr(x, y) memset(x, y, sizeof(x))
 9 #define all(x) x.begin(), x.end()
10 #define pb push_back
11 #define mp make_pair
12 #define MAXN 200005
13 #define fi first
14 #define se second
15 #define SZ(x) ((int)x.size())
16 using namespace std;
17 typedef long long ll;
18 typedef vector<int> vi;
19 typedef pair<int, int> pii;
20 const int INF = 1 << 30;
21 const int p = 1000000009;
22 int lowbit(int x){ return x & (-x);}
23 int fast_power(int a, int b){ int x; for(x = 1; b; b >>= 1){ if(b & 1) x = 1ll * x * a % p; a = 1ll * a * a % p;} return x % p;}
24 
25 int a[MAXN], dp[MAXN][2];
26 
27 int main(){
28     int t;
29     scanf("%d", &t);
30     rep(times, 1, t){
31         int n, p, k;
32         scanf("%d%d%d", &n, &p, &k);
33         rep(i, 1, n) scanf("%d", &a[i]);
34         sort(a + 1, a + n + 1);
35         rep(i, 1, n){
36             dp[i][0] = dp[i - 1][1];
37             dp[i][1] = min(dp[i - 1][0], dp[i - 1][1]) + a[i];
38         }
39         int ans = 0;
40         rep(i, 1, n)
41             if(dp[i][1] <= p) ans = i;
42             else break;
43         printf("%d\n", ans);
44     }
45     return 0;
46 }
View Code

 

B2.K for the Price of One(Hard Version)

题目大意:有n个商品,每个商品价值为  ai  现在有一个活动优惠,买一个物品可以选择k - 1个价值小于等于该它的物品免费获得(要么一个也不选,要么一定要选k - 1个),求k个硬币一共能买多少物品。在该题中k <= n。

做完这道题发现B1写复杂了,其实这两题代码一样的……

主要思路就是贪心+递推。

很显然,若是买了第i个物品,价值小于它的必然要选(不要白不要,而且肯定要最贵的)。

那么我们就得出了递推式 f[i] = f[i - k] + a[i] ,但是注意若i小于等于k,需要的花费为前i个物品的总和。

代码如下:

 

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <cstring>
 5 #include <vector>
 6 #define rep(x, l, r) for(int x = l; x <= r; x++)
 7 #define repd(x, r, l) for(int x = r; x >= l; x--)
 8 #define clr(x, y) memset(x, y, sizeof(x))
 9 #define all(x) x.begin(), x.end()
10 #define pb push_back
11 #define mp make_pair
12 #define MAXN 200005
13 #define fi first
14 #define se second
15 #define SZ(x) ((int)x.size())
16 using namespace std;
17 typedef long long ll;
18 typedef vector<int> vi;
19 typedef pair<int, int> pii;
20 const int INF = 1 << 30;
21 const int p = 1000000009;
22 int lowbit(int x){ return x & (-x);}
23 int fast_power(int a, int b){ int x; for(x = 1; b; b >>= 1){ if(b & 1) x = 1ll * x * a % p; a = 1ll * a * a % p;} return x % p;}
24 
25 int a[MAXN], dp[MAXN];
26 
27 int main(){
28     int t;
29     scanf("%d", &t);
30     rep(times, 1, t){
31         int n, p, k;
32         scanf("%d%d%d", &n, &p, &k);
33         rep(i, 1, n) scanf("%d", &a[i]);
34         sort(a + 1, a + n + 1);
35         int ans = 0;
36         rep(i, 1, n){
37             if(i < k) dp[i] = dp[i - 1] + a[i];
38             else dp[i] = dp[i - k] + a[i];
39         }
40         rep(i, 1, n)
41             if(dp[i] <= p) ans = i;
42         printf("%d\n", ans);
43     }
44     return 0;
45 }
View Code

 

C.Petya and Exam

题目大意:Petya将会参加一场考试,这场考试从时间点0开始,到T结束。考试中有n道题,分为两种,简单(需要花a时间做完)的题和困难(需要花b时间做完)的题(a <= b),即在时间点x开始做这道题,将会在x+a或x+b时间点完成。现在每道题会在时间点 ti 变成必须完成,Petya可以在0 ~ T任意一个时间点离开,若离开时有必须要完成的题目没有完成,他将会得到0分,否则会得到他完成的题目的分数。求他最大能得到的分数。

我们可以得到一个贪心策略,若是在时间点i离开且i+1没有必须要完成的题目,那么在i点离开肯定不如在i+1时间点离开,所以我们只要比较所有 ti - 1 离开能得到的最大分数。

只需要将时间点 ti 排序以后,即可记录下当时需要完成的题目和所要花的时间

另外,对于每一个点,在完成所有需要完成的题目后,肯定要先去做简单的题目,若还有时间再去做困难的题目。

代码如下

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <cstring>
 5 #include <vector>
 6 #define rep(x, l, r) for(int x = l; x <= r; x++)
 7 #define repd(x, r, l) for(int x = r; x >= l; x--)
 8 #define clr(x, y) memset(x, y, sizeof(x))
 9 #define all(x) x.begin(), x.end()
10 #define pb push_back
11 #define mp make_pair
12 #define MAXN 200005
13 #define fi first
14 #define se second
15 #define SZ(x) ((int)x.size())
16 using namespace std;
17 typedef long long ll;
18 typedef vector<int> vi;
19 typedef pair<int, int> pii;
20 const int INF = 1 << 30;
21 const int p = 1000000009;
22 int lowbit(int x){ return x & (-x);}
23 int fast_power(int a, int b){ int x; for(x = 1; b; b >>= 1){ if(b & 1) x = 1ll * x * a % p; a = 1ll * a * a % p;} return x % p;}
24 
25 pii pro[MAXN];
26 int num[MAXN];
27 
28 int main(){
29     int q;
30     scanf("%d", &q);
31     rep(times, 1, q){
32         int n, T, a, b;
33         scanf("%d%d%d%d", &n, &T, &a, &b);
34         int tot0 = 0, tot1 = 0;
35         rep(i, 1, n){
36             scanf("%d", &num[i]);
37             if(!num[i]) tot0++;
38             else tot1++;
39         }
40         rep(i, 1, n){
41             int x;
42             scanf("%d", &x);
43             pro[i] = mp(x, num[i]);
44         }
45         pro[++n] = mp(T + 1, 0);
46         sort(pro + 1, pro + n + 1);
47         int ans = 0, sta0 = 0, sta1 = 0;
48         rep(i, 1, n){
49             if(sta0 > T) break;
50             if(pro[i - 1].fi != pro[i].fi){
51                 int tim = pro[i].fi - 1;
52                 tim -= sta0;
53                 int res = sta1;
54                 if(tim >= 0){
55                     res += min(tim / a, tot0);
56                     tim -= min(tim / a, tot0) * a;
57                     res += min(tim / b, tot1);
58                     ans = max(ans, res);
59                 }
60             }
61             if(pro[i].se){
62                 tot1--;
63                 sta0 += b;
64             }
65             else{
66                 tot0--;
67                 sta0 += a;
68             }
69             sta1++;
70         }
71         printf("%d\n", ans);
72     }
73     return 0;
74 }
View Code

 

D.Enchanted Artifact

题目大意:本题为交互题。有一个字符串s,只由字符'a'和'b'组成。每次你可以询问一个字符串,它会返回这两个字符串的编辑距离。为一个字符串经过修改,删除或插入操作得到另一个字符串,两个字符串编辑距离的定义为最小的操作次数,若返回值为0,那么就是字符串s。让你在n + 2操作内得出字符串s(n为字符串s的长度,未知)。

人生中第一次对交互题有想法,但是是错的想法……

一开始我认为输入'a'和'b',若是原字符串有'a'字符,那么返回的是长度n - 1,否则返回的是长度n,那么原字符串的长度n为返回两值的最小值+1。

然后询问一个由1个'b'和 n - 1 个'a'组成的字符串,共有n种,返回的数一定是字符串中b的个数加1或减1,即该位是否为b,然后因为在询问n +1次后一定要给出字符串s,那么通过前n - 1推出最后一位是否是'b'。

这个方法看上去没问题,但是发现了一组反例。

s字符串为"baaab"

询问的字符串为"aaaba"

根据我的思路返回数应该是3,即字符串中不相同的个数,但是这个数据返回了2。

只需要在首位插入'b',在末位插入'a'即可。

然后我就傻掉了,去看了下题解。

发现题解的思路是先输入300个'a',再输入300个'b',返回的数分别是300 - 'a'的个数以及300 - 'b'的个数,那么就得到了原字符串中'a'和'b'的个数以及字符串的长度。然后将答案串设为全'a',对于每一位将该位为'b',如果返回值小于当前的编辑长度(一开始全'a'的编辑长度就是字符'b'的个数),那么答案的这一位一定是'b'。但是最多询问n + 2次,那么最后一位也只能靠前面答案推出,即若当前编辑长度为1那么最后一位是'b',否则为'a'。

乍一看这个题解思路和我的差不多,但是它不会像我的代码一样出现反例。

为什么呢,因为对于第i位改为'b'时,它前面的字符已经是和字符串s相同了,而我这个反例的最少的编辑操作是将'b'前面的一段往后移动一位,再插入一位比只修改操作要优。但是由于前i - 1位已经相同了就不会有这种操作,那么后面就一定是修改操作。

另外题解中一开始求长度的操作也是优于我的方法的,我的方法很难得出是插入操作还是存在修改操作,所以在n = 2的情况下很难直接得出答案,而题解直接得出了字符串s中'a'和'b'的个数,可以直接给出。

代码如下:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <cstring>
 5 #include <vector>
 6 #define rep(x, l, r) for(int x = l; x <= r; x++)
 7 #define repd(x, r, l) for(int x = r; x >= l; x--)
 8 #define clr(x, y) memset(x, y, sizeof(x))
 9 #define all(x) x.begin(), x.end()
10 #define pb push_back
11 #define mp make_pair
12 #define MAXN
13 #define fi first
14 #define se second
15 #define SZ(x) ((int)x.size())
16 using namespace std;
17 typedef long long ll;
18 typedef vector<int> vi;
19 typedef pair<int, int> pii;
20 const int INF = 1 << 30;
21 const int p = 1000000009;
22 int lowbit(int x){ return x & (-x);}
23 int fast_power(int a, int b){ int x; for(x = 1; b; b >>= 1){ if(b & 1) x = 1ll * x * a % p; a = 1ll * a * a % p;} return x % p;}
24 
25 int judge(string st){
26     puts(st.c_str());
27     fflush(stdout);
28     int ans;
29     scanf("%d", &ans);
30     if(!ans) exit(0);
31     return ans;
32 }
33 
34 int main(){
35     int n = 300;
36     int lena = n - judge(string(n, 'a')),
37         lenb = n - judge(string(n, 'b'));
38     int len = lena + lenb;
39     string ans = string(len, 'a');
40     int res = lenb;
41     rep(i, 0, len - 2){
42         ans[i] = 'b';
43         int s = judge(ans);
44         if(s > res) ans[i] = 'a';
45         else res = s;
46     }
47     if(res) ans[len - 1] = 'b';
48     judge(ans);
49     return 0;
50 }
View Code
posted @ 2020-01-10 08:28  Angel_Kanade  阅读(281)  评论(0编辑  收藏  举报