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 }