codeforce 380(div.2)

 

A B 略

C:二分,贪心

设d(i, v)为 剩余油量为v时,车开距离i 所需要的最小时间,使用线性规划不难算出:

if v < i return INF; //无法到达
if v > 2*i return i;  
if  i <= v <= 2*i return LL(3)*i - v;

那么一辆车开到终点的最短时间等于 ∑d(g[i]-g[i-1], v)
这样只需要二分能开到终点的最小油量,取其中价格最低的即是答案
代码如下:
 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <cstring>
 4 
 5 using namespace std;
 6 #define INF 9999999999LL
 7 const int maxn = 2e5 + 10;
 8 int n, k, s, t;
 9 typedef long long LL ;
10 LL cal(LL i, LL v) {
11     if(v < i) return INF;
12     if(v > 2*i) return i;
13     return LL(3)*i - v;
14 }
15 int c[maxn], v[maxn];
16 int g[maxn];
17 bool judge(LL v) {
18     LL sum = 0;
19     for(int i = 1; i <= k+1; i++) {
20         sum += cal(g[i]-g[i-1], v);
21         if(sum > t) return false;
22     }
23     return true;
24 }
25 int main() {
26     scanf("%d%d%d%d", &n, &k, &s, &t);
27     for(int i = 0; i < n; i++) {
28         scanf("%d%d", &c[i], &v[i]);
29     }
30     g[0] = 0;
31     for(int i = 1; i <= k; i++) {
32         scanf("%d", &g[i]);    
33     }
34     g[k+1] = s;
35     sort(g, g+k+2);
36     LL L = 0, R = INF;
37     while(L < R) {
38         LL M = L + (R-L)/2;
39         if(judge(M)) R = M;
40         else L = M+1;
41     }
42 //    printf("R = %d\n", R);
43     int ans = 1000000010;
44     for(int i = 0; i < n; i++) {
45         if(v[i] >= R) ans = min(ans, c[i]);        
46     }
47     if(ans < 1000000010) printf("%d\n", ans);
48     else puts("-1");
49 }

D: 一道堪比A、B的水题,按顺序输出剩下的可以放船的位置直到不能装下所有船为止

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <utility>
 4 #include <vector>
 5 using namespace std;
 6 const int maxn = 2e5 + 10;
 7 
 8 int n, a, b, k;
 9 char s[maxn];
10 vector<pair<int, int> > vec;
11 vector<int> ans;
12 int main() {
13     scanf("%d%d%d%d%s", &n, &a, &b, &k, s);
14     int cnt = 0;
15     for(int i = 0; i < n; ) {
16         int j = i+1;
17         if(s[i] == '0') {
18             while(s[j] == '0') j++;
19             if(j-i >= b){
20                 vec.push_back(make_pair(i, j));
21                 cnt += (j-i)/b;
22             }
23         }
24         i = j;
25     }
26     int k = cnt - a + 1;
27     printf("%d\n", k);
28     for(int i = 0;k && i < vec.size(); i++) {
29         int l = vec[i].first;
30         int r = vec[i].second;
31         int j = l + b - 1;
32         while(k && j < r) {
33             ans.push_back(j);
34             j += b;
35             k--;
36         }
37     }
38     printf("%d", ans[0]+1);
39     for(int i = 1; i < ans.size(); i++) printf(" %d", ans[i]+1);
40     printf("\n");
41 }

E:贪心,数状数组维护前缀和

分析题意我们可以发现,由于一个下级只能有一个直接上级,一个上级可以有多个直接从属,比如

5 1

0 1 1 1 1 

最小出错数为0;

我们以num[i+1]表示有i个上级的人数 (i+1是为了方便写树状数组),用c[i+1] 来记录上级数为i的情况是否缺少,

c[i+1] = 0 表示 有i个上级的人数不为0,c[i+1] = 1 有i个上级的人数为0,即没有上级数为i的人

用数状数组维护 上级数小于 i 的情况中缺少的总个数。

比如 0 1 1 3 5, 上级数为i之前缺一个上级数为2的情况 ,所以get(3) = 1, 上级数为5之前缺少2 和 4 的情况, 所以get(5) = 2;

设错误人数为cnt,初始为0,用一个队列维护最大位置前缺少的位置

然后从上级数最多的情况开始考虑,

  考虑上级数 为 i时,

    if get(i) = 0   , print cnt   //这时所有的上级数已经连续,  此时cnt就是答案

    else {

      将上级数为i的人一个一个依次填充到之前缺失的位置(相应上级数值),直到num[i] = 0 或者 缺失的位置被填满  

      注意:比最大上级数还要大的位置不需要填,容易出错   

    }

       

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <queue>
 4 using namespace std;
 5 const int maxn = 2e5 + 10;
 6 
 7 int n, s;
 8 int a[maxn];
 9 int c[maxn];
10 int d[maxn];
11 int num[maxn];
12 void add(int x, int v) {
13     while(x < maxn) {
14         d[x] += v;
15         x += x&-x;
16     }
17 }
18 int get(int x) {
19     int ans = 0;
20     while(x) {
21         ans += d[x];
22         x -= x&-x;
23     }
24     return ans;
25 }
26 queue<int> Q;
27 int main(){
28     scanf("%d%d", &n, &s);
29     int cnt = 0;
30     int start = 0;
31     for(int i = 1; i <= n; i++) c[i] = 1;
32     for(int i = 1; i <= n; i++) {
33         scanf("%d", &a[i]);
34         if(i == s && a[i] != 0){
35             cnt++;    
36         } else if(i != s && a[i] == 0) {
37             cnt++;
38         } else {
39             num[a[i]+1]++;
40             c[a[i]+1] = 0;    
41             start = max(start,a[i]+1);
42         }
43     }
44     for(int i = 1; i <= start; i++) if(c[i] == 1) {
45         Q.push(i);    
46     }
47     int k = cnt;
48     while(k && !Q.empty()) {
49         int i = Q.front(); 
50         Q.pop();
51         k--;
52         c[i] = 0;
53         num[i]++;
54     }
55     for(int i = 1; i <= n; i++) add(i, c[i]);
56     for(int i = start; i > 0; i--) {
57         int temp = get(i);
58         if(temp) {
59             int &k = num[i];
60             while(k && !Q.empty()) {
61                 int u = Q.front(); 
62                 Q.pop();
63                 if(u >= i) break;
64                 k--;
65                 c[u] = 0;
66                 add(u, -1);
67                 num[u]++;
68                 cnt++;
69             }    
70         }
71         else {
72             break;
73         }
74     }
75     printf("%d\n", cnt);
76 }

 

 

F: 容易想到DP, 设d(i, j, k)为当前 Igor 先手时的状态,然后记忆化搜索,但是很不幸这样会爆内存 - -||

由于k的值不断增大,经简单计算k最大为64,并且Igor拿去的数量与zhenya拿去的数量之差不超过 64,  i <= n/2 ,

这样我们可以设 d(i, r, k) ,r 为 zhenya比Igor多拿的数量+70(差值可能为负)

maxi = 2100, maxr = 140,  maxk = 70

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #include <vector>
 5 
 6 using namespace std;
 7 const int maxn = 4001;
 8 #define INF 1000000007
 9 #define S(i, j) (sum[j] - sum[i-1])
10 
11 int sum[maxn];
12 int n;
13 
14 int d[2100][140][70];
15 int vis[2100][140][70];
16 int dp(int i, int r, int k) {
17     int j = n - r - (i-1) + 70;
18     //printf("%d %d %d\n", i, j, k);
19     if(i + k - 1> j) return 0;
20     if(vis[i][r][k]) return d[i][r][k];
21     vis[i][r][k] = 1;
22     int &ans = d[i][r][k];
23     ans = -INF;    
24     int temp = S(i, i+k-1);
25     int temp2;
26     if(j-k+1 > i+k-1) {
27         temp2 = -S(j-k+1, j) + dp(i+k, n-(j-k)-(i+k-1)+70, k);
28         if(j-k > i+k-1) {
29             temp2 = min(temp2, -S(j-k, j) + dp(i+k, n-(j-k-1)-(i+k-1)+70, k+1));
30             ans = max(ans, temp+temp2);
31         }
32         else ans = max(ans, temp+temp2);
33     }
34     else ans = max(ans, temp);
35     
36     if(i + k > j) return ans;
37     temp = S(i, i+k);
38     if(j-k > i+k) {
39         temp2 = - S(j-k, j) + dp(i+k+1, n-(j-k-1)-(i+k+1-1)+70, k+1);
40         if(j-k-1 > i+k) {
41             temp2 = min(temp2, -S(j-k-1, j) + dp(i+k+1, n-(j-k-2)-(i+k+1-1)+70, k+2));
42             ans = max(ans, temp+temp2);
43         }
44         else ans = max(ans, temp+temp2);
45     }
46     else ans = max(ans, temp);
47     
48     return ans;
49 }
50 int main() {
51     
52     scanf("%d", &n);
53     sum[0] = 0;
54     for(int i = 1; i <= n; i++) {
55         int a;
56         scanf("%d", &a);
57         sum[i] = sum[i-1] + a;
58     }
59     int ans;
60     ans = dp(1, 70, 1);
61     printf("%d\n", ans);
62     return 0;
63 }

 

posted @ 2016-11-24 11:26  kiraa  阅读(245)  评论(0编辑  收藏  举报