Codeforces Round #510 (Div. 2) A~F
A. Benches
有$n$个座位,一开始第$i$个座位上有$a_i$个人,之后又来了$m$个人,每个人都可以随便坐到一个座位上
设$f$为最终座位上最多的人数,问$f$的最大值和最小值
$1 \le n \le 100, 1 \le m \le 10000, 1 \le a_i \le 100$
最多的话就是往$a_i$最大的那个位置上坐$m$个人
最少的话就先将所有的座位尽可能填补到$\max a_i$,剩下的人就可以依次坐下了
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 int n, m, a[110]; 5 int main() { 6 cin >> n >> m; 7 int mnk, mxk = m; 8 for(int i = 1 ; i <= n ; ++ i) { 9 cin >> a[i]; 10 mxk = max(mxk, a[i] + m); 11 } 12 sort(a + 1, a + 1 + n); 13 ll sum = 0; 14 for(int i = 1 ; i <= n ; ++ i) sum += a[n] - a[i]; 15 if(sum >= m) { 16 mnk = a[n]; 17 } else { 18 m -= sum; 19 mnk = a[n] + m / n + (m % n != 0); 20 } 21 cout << mnk << ' ' << mxk << endl; 22 }
B. Vitamins
有$n$杯果汁,第$i$杯的价钱是$c_i$,每一杯有一个$\{A,B,C}\$的子集,表示它所包含的维生素
你获得一个维生素的前提是喝了至少一被包含这种维生素的果汁
用最小的代价获得所有维生素,如果无解输出$-1$
$1 \le n \le 1000, 1 \le c_i \le 100000$
模拟题意就行了……扔了个装压上去……
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N = 1010; 5 int n, ans[10]; 6 int main() { 7 cin >> n; 8 memset(ans, 0x3f, sizeof ans); 9 ans[0] = 0; 10 for(int i = 1, c ; i <= n ; ++ i) { 11 char s[5]; 12 cin >> c >> s; 13 int b = 0; for(int j = 0 ; s[j] ; ++ j) b |= 1 << (s[j] - 'A'); 14 for(int s = 0 ; s < 8 ; ++ s) { 15 for(int t = s ; ~ t ; t = (t - 1) & s) { 16 if((b | t) == s) 17 ans[s] = min(ans[s], ans[t] + c); 18 if(!t) break; 19 } 20 } 21 } 22 cout << (ans[7] == 0x3f3f3f3f ? -1 : ans[7]) << endl; 23 }
C. Array Product
有一个由$n$个元素构成的序列$a$,每次你可以选择一对$i,j$,满足$1 \le i \le j \le n, i \not= j$,之后将第$j$个元素变为$a_i \times a_j$,并且删掉第$j$个元素
注意当一个元素被删除后其他元素的下标不变,且这个元素视为不可使用
同时你还可以进行最多一次“特殊操作”,既在任意时刻,无条件删除一个元素
显然在操作$n-1$次之后整个序列只剩下一个元素,最大化这个元素的值,同时输出任何一种方案
$2 \le n \le 2 \times 10^5, -10^9 \le a_i \le 10^9$
如果没有“特殊操作”的话,这个序列最终的值一定是一个定值,因为只有乘操作,最终相当于将它们合并起来了
一个比较暴力的做法如下:
如果序列中有$0$,那么先通过操作$1$将$0$变为只有$1$个,之后如果小于$0$的数字的个数是奇数,那么先用这个$0$把最大的那个小于$0$的数变为$0$,然后删掉这个$0$,之后再用操作$1$将剩下的元素都拼接起来;如果没有的话就直接删掉$0$,然后剩下的都用操作$1$拼接起来
如果序列中没有$0$,若小于$0$的个数为奇数,就直接删掉最大的那个,剩下的都用操作$1$乘起来;否则直接都乘起来
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N = 2e5 + 10; 5 struct T { int a, id; } t[N]; 6 bool cmp(T a, T b) { 7 return a.a < b.a; 8 } 9 int n, tot; 10 void tmp() { 11 -- tot; 12 if(!tot) exit(0); 13 } 14 int main() { 15 ios :: sync_with_stdio(0); 16 cin >> n, tot = n; 17 int cnt0 = 0, cntlt0 = 0, cntgt0 = 0; 18 for(int i = 1 ; i <= n ; ++ i) { 19 cin >> t[i].a, t[i].id = i; 20 if(t[i].a == 0) ++ cnt0; 21 else if(t[i].a < 0) ++ cntlt0; 22 else ++ cntgt0; 23 } 24 sort(t + 1, t + 1 + n, cmp); 25 for(int i = 1 ; i <= n ; ++ i) { 26 if(t[i].a == 0) { 27 for(int j = i + 1 ; j <= n ; ++ j) { 28 if(t[j].a == 0) { 29 tmp(); 30 printf("1 %d %d\n", t[j].id, t[i].id); 31 t[j].a = 1e9 + 10; 32 } else break; 33 } 34 break; 35 } 36 } 37 sort(t + 1, t + 1 + n, cmp); 38 while(n >= 1 && t[n].a == 1e9 + 10) -- n; 39 if(n) { 40 if(cnt0) { 41 if(cntlt0 & 1) { 42 for(int i = 1 ; i <= n ; ++ i) { 43 if(t[i].a == 0) { 44 if(i - 1 >= 1) { 45 tmp(); 46 printf("1 %d %d\n", t[i - 1].id, t[i].id); 47 t[i - 1].a = 1e9 + 10; 48 } 49 tmp(); 50 printf("2 %d\n", t[i].id); 51 t[i].a = 1e9 + 10; 52 break; 53 } 54 } 55 } else { 56 for(int i = 1 ; i <= n ; ++ i) { 57 if(t[i].a == 0) { 58 tmp(); 59 printf("2 %d\n", t[i].id); 60 t[i].a = 1e9 + 10; 61 break; 62 } 63 } 64 } 65 } else { 66 if(cntlt0 & 1) { 67 for(int i = n ; i ; -- i) { 68 if(t[i].a < 0) { 69 tmp(); 70 printf("2 %d\n", t[i].id); 71 t[i].a = 1e9 + 10; 72 break; 73 } 74 } 75 } 76 } 77 sort(t + 1, t + 1 + n, cmp); 78 while(n >= 1 && t[n].a == 1e9 + 10) -- n; 79 for(int i = 1 ; i < n ; ++ i) { 80 int x = t[i + 1].id; 81 tmp(); 82 printf("1 %d %d\n", t[i].id, x); 83 } 84 } 85 }
D. Petya and Array
给定一个序列,问有多少连续子序列的和小于$t$
$1 \le n \le 200000, |t| \le 2 \times 10^{14}, |a_i| \le 10^9$
求一下前缀和$s$,也就是$s_i-s_j \le t-1 \to s_j \ge t-1-s_i$
离散化后树状数组查询前/后缀和就行了
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N = 2e5 + 10; 5 int n; ll a[N], s[N], t; 6 vector<ll> num; 7 int get(ll n) { 8 return lower_bound(num.begin(), num.end(), n) - num.begin() + 1; 9 } 10 int bit[int(1e6)]; 11 void add(int x) { 12 for( ; x <= num.size() + 10 ; x += x & -x) 13 ++ bit[x]; 14 } 15 int ask(int x) { 16 int r = 0; 17 for( ; x ; x -= x & -x) r += bit[x]; 18 return r; 19 } 20 21 int main() { 22 ios :: sync_with_stdio(0), cin.tie(0), cout.tie(0); 23 cin >> n >> t; -- t; 24 num.push_back(0); 25 for(int i = 1 ; i <= n ; ++ i) cin >> a[i], s[i] = s[i - 1] + a[i], num.push_back(s[i]), num.push_back(s[i] - t - 1); 26 sort(num.begin(), num.end()); 27 add(get(0)); 28 ll ans = 0; 29 for(int i = 1 ; i <= n ; ++ i) { 30 ans += i - ask(get(s[i] - t - 1)); 31 add(get(s[i])); 32 } 33 cout << ans << endl; 34 }
E. Vasya and Magic Matrix
给定一个$n \times m$的矩阵$a$,每个位置都有一个值$a_{i,j}$,对于每一个点$(i,j)$,它可以走到一个小于$a_{i,j}$的位置(等概率的),设其为$(x,y)$,同时花费$(x-i)^2+(y-j)^2$的代价,然后继续这个过程,直到走不动了
同时给$(r,c)$,问在$(r,c)$时花费的代价的期望
$1 \le n,m \le 1000, 0 \le a_{i,j} \le 10^9, 1 \le r \le n, 1 \le m \le c$
如果直接做我是不会的……
由于有关于$a$的大小限制,那么不妨尝试一种套路——按照$a$升序排序,然后依次填入到矩阵中
假设当前考虑到了$(x,y)$, 之前已经填入了$k$个节点,那么当前点的答案就是
$$ \begin{aligned} &\frac{1}{k}\sum_{i=1}^{k}f_{i}+(x_i-x)^2+(y_i-y)^2 \\ =&\frac{1}{k}\sum_{i=1}^{k}f_{i}+x_i^2+x^2-2xx_i+y_i^2+y^2-2yy_i \\ =&\frac{(\sum_{i=1}^{k}f_{i})+(\sum_{i=1}^{k}x_i^2)+kx^2-2x(\sum_{i=1}^{k}x_i)+(\sum_{i=1}^{k}y_i^2)+ky^2-2y(\sum_{i=1}^{k}y_i)}{k} \end{aligned} $$
然后做完了……动态维护一下这几个变量就行了……
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N = 1010, mod = 998244353; 5 int n, m, r, c; struct P {int val, x, y; } p[N * N]; 6 bool operator < (P a, P b) { return a.val < b.val; } 7 ll f[N * N], Ti, Tj, Tii, Tjj, sum, cnt; 8 ll pw(ll a, ll b) { 9 ll r = 1; 10 for( ; b ; b >>= 1, a = a * a % mod) 11 if(b & 1) 12 r = r * a % mod; 13 return r; 14 } 15 16 ll pls(ll a = 0, ll b = 0, ll c = 0) { 17 return ((a + b) % mod + c) % mod; 18 } 19 20 int main() { 21 ios :: sync_with_stdio(0); 22 cin >> n >> m; int tot = 0; 23 for(int i = 1 ; i <= n ; ++ i) 24 for(int j = 1 ; j <= m ; ++ j) 25 cin >> p[++ tot].val, p[tot].x = i, p[tot].y = j; 26 cin >> r >> c; 27 sort(p + 1, p + 1 + tot); 28 for(int i = 1 ; i <= tot ; ++ i) { 29 ll p1 = sum; 30 ll p2 = Tii; 31 ll p3 = Tjj; 32 ll p4 = 1ll * cnt * p[i].x % mod * p[i].x % mod; 33 ll p5 = 1ll * cnt * p[i].y % mod * p[i].y % mod; 34 ll p6 = -2ll * p[i].x % mod * Ti % mod; 35 ll p7 = -2ll * p[i].y % mod * Tj % mod; 36 f[i] = pls(pls(p1, p2, p3), pls(p4, p5), pls(p6, p7)) * pw(cnt, mod - 2) % mod; 37 if(p[i].x == r && p[i].y == c) { 38 f[i] = (f[i] % mod + mod) % mod; 39 cout << f[i] << endl; 40 exit(0); 41 } 42 if(p[i].val != p[i + 1].val) { 43 for(int j = i ; j >= 1 ; -- j) { 44 if(p[j].val != p[i].val) break; 45 Ti += p[j].x, Tj += p[j].y; 46 Tii += 1ll * p[j].x * p[j].x % mod, Tjj += 1ll * p[j].y * p[j].y % mod; 47 Ti %= mod, Tj %= mod, Tii %= mod, Tjj %= mod; 48 sum += f[j], sum %= mod; 49 ++ cnt; 50 } 51 } 52 } 53 }
F. Leaf Sets
给定一棵$n$个节点的无根树,将它的叶子分成若干块,使得每一块叶子的两两之间距离的最大值不超过$k$
最小化分割的块数
$3 \le n \le 10^6, 1 \le k \le 10^6$
首先需要找到一个非叶子结点,然后用这个节点当作根开始$dfs$
每个节点都维护一个$multiset$,表示每一块的最深深度
然后直接启发式合并一下就行了……
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N = 1e6 + 10; 5 multiset<int> s[N]; 6 int n, k; vector<int> g[N]; 7 void dfs(int u, int fa, int dep) { 8 if(g[u].size() == 1) return s[u].insert(dep), void(); 9 int c = 0; for(int v: g[u]) 10 if(v != fa) 11 dfs(v, u, dep + 1), 12 (c == 0 || s[c].size() < s[v].size() ? (c = v) : 0); 13 swap(s[c], s[u]); 14 for(int v: g[u]) 15 if(v != fa && v != c) 16 for(int d: s[v]) { 17 int x = k + 2 * dep - d; 18 auto it = s[u].upper_bound(x); 19 if(it == s[u].begin()) s[u].insert(d); 20 else { 21 int x = * -- it; 22 s[u].erase(it); 23 s[u].insert(max(x, d)); 24 } 25 } 26 } 27 int main() { 28 ios :: sync_with_stdio(0); 29 cin >> n >> k; 30 for(int i = 1, u, v ; i < n ; ++ i) 31 cin >> u >> v, 32 g[u].push_back(v), 33 g[v].push_back(u); 34 for(int i = 1 ; i <= n ; ++ i) 35 if(g[i].size() >= 2) 36 return dfs(i, 0, 0), 37 printf("%d\n", int(s[i].size())), 0; 38 }