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 }