codeforces round#510
A. Benches
Description
给出$N$个座位, 每个座位上初始有$a_i$ 个人, 这些人都不能移动。
另外还有$M$个人, 要求让他们坐到座位上, $max$ 为所有座位上人数最多 的 人数。
求出 $max$ 的可能最大值和 最小值。
Solution
最大值就是 给出的最多的人数 的座位 加上 $M$
最小值可以二分答案, 容易求出
Code
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define rd read() 5 using namespace std; 6 7 const int N = 1e3; 8 9 int n, m, a[N], maxn; 10 11 int read() { 12 int X = 0, p = 1; char c = getchar(); 13 for(; c > '9' || c < '0'; c = getchar()) if(c == '-') p = -1; 14 for(;c >= '0' && c <= '9'; c = getchar()) X = X * 10 + c - '0'; 15 return X * p; 16 } 17 18 bool jud(int minn) { 19 int rest = m; 20 for(int i = 1; i <= n; ++i) { 21 if(a[i] > minn) return false; 22 rest -= minn - a[i]; 23 } 24 if(rest > 0) return false; 25 else return true; 26 } 27 28 int main() 29 { 30 n = rd; m = rd; 31 for(int i = 1; i <= n; ++i) 32 a[i] = rd; 33 for(int i = 1; i <= n; ++i) 34 maxn = max(maxn, a[i]); 35 int l = 0, r = 2e4 + 5, ans = 0; 36 while(l <= r) { 37 int mid = (l + r) >> 1; 38 if(jud(mid)) r = mid - 1, ans = mid; 39 else l = mid + 1; 40 } 41 printf("%d %d\n", ans, maxn + m); 42 }
B. Vitamins
Description
给出$N$种维生素药, 第$a_i$种药包含了$A,B,C,$中的其中一种或多种维生素, 每一种药都有其价格$cost_i$
现要求出 能吃到维生素 $A,B,C$ 至少花费多少钱。
Solution
显然, 药最多只有7种, 用$2$进制 $S$ 表示是否含有某种维生素, 该种药的最小价格记为 $minn[S]$
在输入时,我们把第 $i$ 种药 更新到对应的 含有维生素的集合S。
求最后的答案时, 只需要三种枚举就行了:
$1$ : 买一种药, 对应集合 $111(B)$
$2$ : 买两种药 $i$ 和 $j$, 并且$i \ | \ j \ = \ 111(B)$
$3$: 买三种药 $i$ 和 $j$, 并且$i \ | \ j \ | \ k \ = \ 111(B)$
求其中的最小值即可。
Code
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 6 const int N = 1e3 + 5; 7 const int inf = ~0U >> 4; 8 9 int n, v[10], minn[10], ans = inf; 10 11 char s[10]; 12 13 struct node { 14 int cost, vis; 15 }a[N]; 16 17 int main() 18 { 19 for(int i = 0; i < 10; ++i) 20 minn[i] = inf; 21 scanf("%d", &n); 22 for(int i = 1; i <= n; ++i) { 23 memset(s, 0,sizeof(s)); 24 memset(v, 0, sizeof(v)); 25 scanf("%d%s", &a[i].cost, s); 26 for(int j = 0, len = strlen(s); j < len; ++j) 27 a[i].vis |= 1 << (s[j] - 'a'); 28 minn[a[i].vis] = min(minn[a[i].vis], a[i].cost); 29 } 30 ans = min(ans, minn[7]); 31 for(int i = 1; i < 8; ++i) 32 for(int j = 1; j < 8; ++j) 33 if((i | j) == 7) ans = min(ans, minn[i] + minn[j]); 34 for(int i = 1; i < 8; ++i) 35 for(int j = 1; j < 8; ++j) 36 for(int k = 1; k < 8; ++k) 37 if((i | j | k) == 7) 38 ans = min(ans, minn[i] + minn[j] + minn[k]); 39 printf("%d\n", ans == inf ? -1 : ans); 40 }
C. Array Product
Description
给出一个有$N$项的序列${a}$, 有两种操作
$1$: 选择两个数 $i$ $j$, 把$j$ 位置上的数更新为$a_j * a_i$, 将$i$ 位置上的数删去(删去后不能再进行操作)
$2$: 选择一个数$i$, 将 $i$ 位置上的数直接删去 ( 该种操作最多只能进行$1$次)
问如何操作才能使 $N-1$ 次操作后剩下的那个数最大, 输出操作的方案。
Solution
大佬说这是前四题最不可做的, 所以我 先水了D题, 最后还WA了两次才 在1:52的时候过(好像我rank并没有升多少
有如下4中情况:
$1$ : 序列中有 奇数个 负数, 并且 存在 $0$—— 将所有的 $0$ 合并成一个,再把 $0$ 和 绝对值最小的 负数 合并 得到 $0$, 删去这个 $0$ 后 把剩余的数合并。
$2$ : 序列中有 奇数个 负数, 并且 不存在 $0$—— 直接删除 绝对值最小的负数, 剩下的合并。
$3$ : 序列中有 偶数个 负数, 并且存在 $0$—— 把所有的$0$ 合并成一个, 把 $0$ 删去, 剩下的数合并
$4$ : 序列中有 偶数个负数, 并且 不存在 $0$—— 直接合并。
Code
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #define rd read() 6 using namespace std; 7 8 const int N = 2e5 + 5; 9 const int inf = ~0U >> 1; 10 11 int a[N], vis[N], tot, n, rest; 12 13 vector<int> cur0, cur1; 14 15 int read() { 16 int X = 0, p = 1; char c = getchar(); 17 for(; c > '9' || c < '0'; c = getchar()) if(c == '-') p = -1; 18 for(; c >= '0' && c <= '9'; c = getchar()) X = X * 10 + c - '0'; 19 return X * p; 20 } 21 22 int main() 23 { 24 n = rd; 25 rest = n - 1; 26 a[0] = -inf; 27 for(int i = 1; i <= n; ++i) { 28 a[i] = rd; 29 if(a[i] < 0) tot++; 30 if(a[i] < 0) cur1.push_back(i); 31 if(a[i] == 0) cur0.push_back(i); 32 } 33 /*if((tot & 1)) { 34 int pos = 0; 35 for(int i = 0, len = cur1.size(); i < len; ++i) 36 if(a[pos] < a[cur1[i]]) pos = cur1[i]; 37 vis[pos] = 1; 38 printf("2 %d\n", pos); 39 rest--; 40 } 41 for(int i = 0, len = cur0.size(); i < len - 1 && rest; ++i) { 42 vis[cur0[i]] = 1; 43 printf("1 %d %d\n", cur0[i], cur0[i + 1]); 44 rest--; 45 } 46 if(tot % 2 == 0 && cur0.size() && rest) { 47 printf("2 %d\n", cur0[(int)cur0.size() - 1]); 48 vis[cur0[(int)cur0.size() - 1]] = 1; 49 rest --; 50 }*/ 51 52 if(tot % 2 && cur0.size()) { 53 for(int i = 0, len = cur0.size(); i < len - 1 && rest; ++i) { 54 vis[cur0[i]] = 1; 55 printf("1 %d %d\n", cur0[i], cur0[i + 1]); 56 rest--; 57 } 58 int pos = 0; 59 for(int i = 0, len = cur1.size(); i < len; ++i) 60 if(a[pos] < a[cur1[i]]) pos = cur1[i]; 61 printf("1 %d %d\n", cur0[(int)cur0.size() - 1], pos); 62 rest--; 63 vis[cur0[(int)cur0.size() - 1]] = 1; 64 if(rest) { 65 printf("2 %d\n", pos); 66 vis[pos] = 1; 67 rest--; 68 } 69 } 70 else if(cur0.size()) { 71 for(int i = 0, len = cur0.size(); i < len - 1 && rest; ++i) { 72 vis[cur0[i]] = 1; 73 printf("1 %d %d\n", cur0[i], cur0[i + 1]); 74 rest--; 75 } 76 if(rest) { 77 printf("2 %d\n", cur0[(int)cur0.size() - 1]); 78 vis[cur0[(int)cur0.size() - 1]] = 1; 79 rest --; 80 81 } 82 } 83 else if(!cur0.size() && tot % 2) { 84 int pos = 0; 85 for(int i = 0, len = cur1.size(); i < len; ++i) 86 if(a[pos] < a[cur1[i]]) pos = cur1[i]; 87 printf("2 %d\n", pos); 88 rest--; 89 vis[pos] = 1; 90 } 91 for(int i = 1, j = 1; rest;) { 92 while(vis[i] && i <= n) i++; 93 j = i + 1; 94 while(vis[j] && j <= n) j++; 95 printf("1 %d %d\n", i, j); 96 vis[i] = 1; 97 rest--; 98 } 99 100 }
D. Petya and Array
Description
求出有多少个 子段的 和 $<t$
Solution
跟树状数组求逆序对一样的原理。
我不会用语言表述 $QuQ$ (╥╯^╰╥)
简单说, 就是先求出前缀和$sum$, 当前我们需要求出以$i$ 为结尾的子段有多少个满足$sum[i] \ - \ sum[j] \ < \ t$,$0<=j<i$
这样而树状数组求静态逆序对就相当于 $t = 0$时的做法了。
Code
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define rd read() 5 #define ll long long 6 using namespace std; 7 8 const int N = 2e5 + 5; 9 10 int n, a[N]; 11 ll qsum[N], t, b[N], tot; 12 ll sum[N], ans; 13 14 ll read() { 15 ll X = 0, p = 1; char c = getchar(); 16 for(; c > '9' || c < '0'; c = getchar()) if(c == '-') p = -1; 17 for(; c >= '0' && c <= '9'; c = getchar()) X = X * 10 + c - '0'; 18 return X * p; 19 } 20 21 int fd(ll x) { 22 int tmp = lower_bound(b + 1, b + 1 + tot, x) - b; 23 if(b[tmp] == x) return tmp; 24 else return tmp - 1; 25 } 26 27 int lowbit(int x) { 28 return x & -x; 29 } 30 31 ll query(int x) { 32 ll re = 0; 33 for(; x; x -= lowbit(x)) re += sum[x]; 34 return re; 35 } 36 37 void add(int x) { 38 for(; x <= tot; x += lowbit(x)) 39 sum[x]++; 40 } 41 42 int main() 43 { 44 n = rd; t = rd; 45 for(int i = 1; i <= n; ++i) 46 a[i] = rd; 47 for(int i = 1; i <= n; ++i) { 48 qsum[i] = qsum[i - 1] + a[i]; 49 b[++tot] = qsum[i]; 50 } 51 b[++tot] = 0; 52 sort(b + 1, b + 1 + tot); 53 tot = unique(b + 1, b + 1 + tot) - b - 1; 54 add(fd(0)); 55 for(int i = 1; i <= n; ++i) { 56 int tmp = fd(qsum[i] - t); 57 ans += (i - query(tmp)); 58 tmp = fd(qsum[i]); 59 add(tmp); 60 } 61 printf("%I64d\n", ans); 62 }