Codeforces Round #610 (Div. 2) 前5题题解
感觉这场比赛质量不错,出题人真的良心,样例数据给那么详细。
这次我还第一次遇到了ILE(Idleness limit exceeded),原来fflushu(stdout)是每次输出后都要用的……
比赛传送门
A.Temporarily unavailable
题目大意:有一条数轴,要从a点跑到b点,在c点的地方有网络,覆盖范围为半径为r的圆。问在多少时间有网路覆盖。
因为是在数轴上的,所以有网路覆盖的地方就是(c - r,c + r),那么只要减一下就好,注意可能有些区域在跑的地方的外面。
代码如下:
1 #include2 #include 3 #include 4 #include 5 #include 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 }
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 #include2 #include 3 #include 4 #include 5 #include 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 }
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 #include2 #include 3 #include 4 #include 5 #include 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 }
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 #include2 #include 3 #include 4 #include 5 #include 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 }
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 #include2 #include 3 #include 4 #include 5 #include 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 }