Codeforces ZeptoLab Code Rush 2015

2015-04-05 20:03:53

总结: 周六晚上的两连击... bc + cf 累= =、

  a、b两题仔细看懂题意都不难,c题是个部分贪心问题,出现过不止一次了吧。。

  赛后补了d和e,都不是非常难的那种。

  

A:严格地选5个等距的'*',cha点在于有些人没选够5个点(... 无语了)

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <cmath>
 5 #include <vector>
 6 #include <map>
 7 #include <set>
 8 #include <stack>
 9 #include <queue>
10 #include <string>
11 #include <iostream>
12 #include <algorithm>
13 using namespace std;
14 
15 #define MEM(a,b) memset(a,b,sizeof(a))
16 #define REP(i,n) for(int i=0;i<(n);++i)
17 #define FOR(i,a,b) for(int i=(a);i<=(b);++i)
18 #define getmid(l,r) ((l) + ((r) - (l)) / 2)
19 #define MP(a,b) make_pair(a,b)
20 
21 typedef long long ll;
22 typedef pair<int,int> pii;
23 const int INF = (1 << 30) - 1;
24 
25 int n;
26 char s[1000];
27 
28 int main(){
29     scanf("%d",&n);
30     scanf("%s",s + 1);
31     int top = n / 4;
32     bool flag = false;
33     for(int l = 1; l <= top; ++l){
34         for(int st = 1; st <= n; ++st){
35             if(st + 4 * l > n) break;
36             if(s[st] == '*' && s[st + l] == '*' && s[st + 2 * l] == '*'
37                     && s[st + 3 * l] == '*' && s[st + 4 * l] == '*'){
38                 flag = true;
39                 break;
40             }
41         }
42     }
43     if(flag) printf("yes\n");
44     else printf("no\n");
45     return 0;
46 }
View Code

 

B:从叶子开始往上考虑即可。只要观察到 2*n-1 和 2*n 是一对兄弟节点就行。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <cmath>
 5 #include <vector>
 6 #include <map>
 7 #include <set>
 8 #include <stack>
 9 #include <queue>
10 #include <string>
11 #include <iostream>
12 #include <algorithm>
13 using namespace std;
14 
15 #define MEM(a,b) memset(a,b,sizeof(a))
16 #define REP(i,n) for(int i=0;i<(n);++i)
17 #define FOR(i,a,b) for(int i=(a);i<=(b);++i)
18 #define getmid(l,r) ((l) + ((r) - (l)) / 2)
19 #define MP(a,b) make_pair(a,b)
20 
21 typedef long long ll;
22 typedef pair<int,int> pii;
23 const int INF = (1 << 30) - 1;
24 
25 int n,num;
26 ll v[100000];
27 
28 int main(){
29     scanf("%d",&n);
30     num = (1 << (n + 1)) - 2;
31     for(int i = 1; i <= num; ++i) scanf("%I64d",&v[i]);
32     ll ans = 0;
33     for(int i = num; i >= 1; i -= 2){
34         if(i * 2 + 1 <= num){
35             v[i] += v[i * 2 + 1];
36         }
37         if((i - 1) * 2 + 1 <= num){
38             v[i - 1] += v[(i - 1) * 2 + 1];
39         }
40         if(v[i] < v[i - 1]){
41             ans += v[i - 1] - v[i];
42             v[i] = v[i - 1];
43         }
44         else if(v[i] > v[i - 1]){
45             ans += v[i] - v[i - 1];
46             v[i - 1] = v[i];
47         }
48     }
49     printf("%I64d\n",ans);
50     return 0;
51 }
View Code

 

C:搞了好久好久... 题目数据非常大,显然不能用背包直接搞。

  设red拿了x个,blue拿了y个,我们可以列出方程:wr * x + wb * y <= C ; 0 <= x <= C / wr ; 0 <= y <= C / wb

  要求 ans = hr * x + hb * y 最大。吐槽:是一个比较明显的线性规划问题,其实高中接触过这种问题。

思路:现在出成了编程题,且数据很大,考虑用部分贪心来解决。相关概念参考08年《部分贪心在信息学竞赛中的应用》

  简单来说就是对于大数据,采用贪心方法将其范围缩小,再采用其他方法求解。

  首先考虑性价比,肯定优先选性价比高的。其次我们可以将C分块,每块Lcm(wr,wb),显然每块都放性价比高的candy,且不会有剩余重量。

  最后会剩余一点重量m,设 C = k*lcm + m,我们将 C 分成:(k-1) * lcm 和 lcm + m 两部分。

    (关于为什么这么分 C,我们可以反证,假设我们需要考虑 2 * lcm + m,那么这部分 2*lcm <= wr*x + wb*y <= 2*lcm + m,

       那么肯定有 wr*x 或 wb*y 大于等于 lcm,如果wr*x >= lcm,那么自然正确;

     如果wb*y >= lcm,把全放wb的lcm和wb*y - lcm分开,发现显然并非最优解。) 

  我们只要在 lcm + m 的重量里枚举有多少red和blue,求出最优解即可。在枚举时我们要枚举重量较大的,以防超时。(比如wr=1,wb=1000000000这种)

  (其实范围恰当的暴力枚举也可以,参见rng_58:http://codeforces.com/contest/526/submission/10578070

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <cmath>
 5 #include <vector>
 6 #include <map>
 7 #include <set>
 8 #include <stack>
 9 #include <queue>
10 #include <string>
11 #include <iostream>
12 #include <algorithm>
13 using namespace std;
14 
15 #define MEM(a,b) memset(a,b,sizeof(a))
16 #define REP(i,n) for(int i=0;i<(n);++i)
17 #define FOR(i,a,b) for(int i=(a);i<=(b);++i)
18 #define getmid(l,r) ((l) + ((r) - (l)) / 2)
19 #define MP(a,b) make_pair(a,b)
20 
21 typedef long long ll;
22 typedef pair<int,int> pii;
23 const int INF = (1 << 30) - 1;
24 
25 ll c,hr,hb,wr,wb,ans;
26 
27 ll Gcd(ll a,ll b){
28     return b == 0 ? a : Gcd(b,a % b);
29 }
30 
31 ll Lcm(ll a,ll b){
32     return a / Gcd(a,b) * b;
33 }
34 
35 int main(){
36     scanf("%I64d%I64d%I64d%I64d%I64d",&c,&hr,&hb,&wr,&wb);
37     ll lcm = Lcm(wr,wb);
38     ll m = c % lcm,k = c / lcm;
39     if(k != 0) k--,m += lcm;
40     k *= lcm;
41     if(hr * wb > hb * wr) ans = k / wr * hr;
42     else ans = k / wb * hb;
43     if(wr < wb){
44         swap(hr,hb);
45         swap(wr,wb);
46     }
47     ll res = 0;
48     for(ll i = 0; i * wr <= m; ++i){
49         ll cur = i * hr + (m - i * wr) / wb * hb;
50         res = max(res,cur);
51     }
52     printf("%I64d\n",ans + res);
53     return 0;
54 }
View Code

 

D:题解讲的很好... 

    如果把输入的String看成:SSS...SST(相当于把A、B打包成S),那么我们可以通过经典KMP求出S(即最小循环节)

  然后分两种情况:

  (1)T = S:此时String = SSS...SSS,设总共R个S,我们需要检查其能否分出K个S和一个A,利用贪心方法,我们算出A的最小长度:R % K 个S

      那么B的长度就为:R / K - R % K,最后检查一下B的长度是否 >= 0 即可。

  (2)T != S:和(1)的类似,但是这时A、B的长度不能为0(因为T!= S,且 T ∈ A)所以检查 R / K - R % K > 0。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <cmath>
 5 #include <vector>
 6 #include <map>
 7 #include <set>
 8 #include <stack>
 9 #include <queue>
10 #include <string>
11 #include <iostream>
12 #include <algorithm>
13 using namespace std;
14 
15 #define MEM(a,b) memset(a,b,sizeof(a))
16 #define REP(i,n) for(int i=0;i<(n);++i)
17 #define FOR(i,a,b) for(int i=(a);i<=(b);++i)
18 #define getmid(l,r) ((l) + ((r) - (l)) / 2)
19 #define MP(a,b) make_pair(a,b)
20 
21 typedef long long ll;
22 typedef pair<int,int> pii;
23 const int INF = (1 << 30) - 1;
24 
25 int n,k;
26 int P[1000010];
27 char s[1000010];
28 
29 int Get_next(){
30     P[0] = -1;
31     int j = -1;
32     for(int i = 1; i < n; ++i){
33         while(j >= 0 && s[j + 1] != s[i]) j = P[j];
34         if(s[j + 1] == s[i]) j++;
35         P[i] = j;
36     }
37 }
38 
39 int main(){
40     scanf("%d%d",&n,&k);
41     scanf("%s",s);
42     Get_next();
43     REP(i,n){
44         int cur = i - P[i];
45         int L = (i + 1) % cur;
46         int cnt = (i + 1) / cur;
47         if(L == 0){
48             if(cnt / k - cnt % k >= 0) printf("1");
49             else printf("0");
50         }
51         else{
52             if(cnt / k - cnt % k > 0) printf("1");
53             else printf("0");
54         }
55     }
56     puts("");
57     return 0;
58 }
View Code

 

E:简单来说就是把n个环形数分为m组,使得每一份的数字和不超过b,求最小的m。每次查询给出b。

  跪Jay巨... 20min直接 dp + two points 搞掉了。(题解借鉴了JayYe的程序~)

  设dp[i]为前 i 个数需要的最少组数。我们发现如果用 two points 扫描的话,前 i 个数的分组情况的最优解是唯一的。(此时最右边的分组一定尽量饱和,思考)

  所以对于每一个dp[i],它的第一组的个数是确定的,比如4个数:1 5 5 9 ,b = 10,在 i = 3 时的分组情况是 (1)、(5,5),第一组就一个数。

  由于数字是环形的,所以第一组可能包含末尾的几个数,恰恰每个dp[i]的第一组已经确定,我们可以检查:sum[n] - sum[i] + prefix[i] <= b,如果符合 dp[i]是一个解。

  (sum[]数组保存前缀和,prefix[i]表示前 i 个数第一组内数的总合)

  转移方程:dp[i] = dp[pre] + 1 , prefix[i] = prefix[pre] , pre 为 two points 的左指针。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <cmath>
 5 #include <vector>
 6 #include <map>
 7 #include <set>
 8 #include <stack>
 9 #include <queue>
10 #include <string>
11 #include <iostream>
12 #include <algorithm>
13 using namespace std;
14 
15 #define MEM(a,b) memset(a,b,sizeof(a))
16 #define REP(i,n) for(int i=0;i<(n);++i)
17 #define FOR(i,a,b) for(int i=(a);i<=(b);++i)
18 #define getmid(l,r) ((l) + ((r) - (l)) / 2)
19 #define MP(a,b) make_pair(a,b)
20 
21 typedef long long ll;
22 typedef pair<int,int> pii;
23 const int INF = (1 << 30) - 1;
24 const int MAXN = 1000010;
25 
26 int n,q,b;
27 int v[MAXN];
28 int dp[MAXN];
29 ll sum[MAXN],P[MAXN];
30 
31 int main(){
32     scanf("%d%d",&n,&q);
33     FOR(i,1,n){
34         scanf("%d",&v[i]);
35         sum[i] = sum[i - 1] + v[i];
36     }
37     FOR(o,1,q){
38         scanf("%d",&b);
39         ll cur = 0;
40         int ans = INF,pre = 1;
41         for(int i = 1; i <= n; ++i){
42             cur += v[i];
43             while(cur > b) cur -= v[pre++];
44             dp[i] = dp[pre - 1] + 1;
45             if(pre == 1) P[i] = sum[i];
46             else P[i] = P[pre - 1]; //统计1到起点的前缀和
47             if(sum[n] - sum[i] + P[i] <= b) ans = min(ans,dp[i]);
48         }
49         ans = min(ans,dp[n]);
50         printf("%d\n",ans);
51     }
52     return 0;
53 }
View Code

 

转载于:https://www.cnblogs.com/naturepengchen/articles/4394855.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值