Codeforces ZeptoLab Code Rush 2015
2015-04-05 20:03:53
总结: 周六晚上的两连击... bc + cf 累= =、
a、b两题仔细看懂题意都不难,c题是个部分贪心问题,出现过不止一次了吧。。
赛后补了d和e,都不是非常难的那种。
A:严格地选5个等距的'*',cha点在于有些人没选够5个点(... 无语了)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
B:从叶子开始往上考虑即可。只要观察到 2*n-1 和 2*n 是一对兄弟节点就行。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
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)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
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。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
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 的左指针。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }