Codeforces Round #538 (Div. 2)
A. Got Any Grapes?
签.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int x, y, z, a, b, c; 5 bool solve() 6 { 7 if (a < x) return false; 8 a -= x; 9 b += a; 10 if (b < y) return false; 11 b -= y; 12 c += b; 13 if (c < z) return false; 14 return true; 15 } 16 17 int main() 18 { 19 while (scanf("%d%d%d", &x, &y, &z) != EOF) 20 { 21 scanf("%d%d%d", &a, &b, &c); 22 puts(solve() ? "YES" : "NO"); 23 } 24 return 0; 25 }
B. Yet Another Array Partitioning Task
Solved.
题意:
有一个序列, 要求把这个序列分成$k段,每段最少m个$
要求每一段中的前$m$大的数加起来的总和最大
输出总和以及分配的方案
注意这里分的序列是连续的
思路:
我们考虑答案的总和就是所有数排序后的前$m * k大之和$
$我们考虑一个不属于前m * k大的数$
$首先我们考虑每一段中都有m个数$
$并且这m个数属于前m * k大的$
$那么剩下的数,无论分到哪个组,都不会被统计进答案$
$那么只要贪心选取,标记哪些数是前m * k大$
$每个组只要够了m个这样的数,就开启下一组$
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 #define N 200010 6 int n, m, k; 7 int vis[N]; 8 struct node 9 { 10 int a, id; 11 void scan(int id) 12 { 13 this->id = id; 14 scanf("%d", &a); 15 } 16 bool operator < (const node &other) const 17 { 18 return a > other.a; 19 } 20 }a[N]; 21 22 int main() 23 { 24 while (scanf("%d%d%d", &n, &m, &k) != EOF) 25 { 26 memset(vis, 0, sizeof vis); 27 for (int i = 1; i <= n; ++i) a[i].scan(i); 28 ll res = 0; 29 sort(a + 1, a + 1 + n); 30 for (int i = 1; i <= m * k; ++i) res += a[i].a, vis[a[i].id] = 1; 31 vector <int> vec; 32 for (int i = 1, tmp = 0; i <= n; ++i) 33 { 34 tmp += vis[i]; 35 if (tmp == m) 36 { 37 vec.push_back(i); 38 tmp = 0; 39 } 40 } 41 printf("%lld\n", res); 42 for (int i = 0; i < k - 1; ++i) 43 printf("%d%c", vec[i], " \n"[i == k - 2]); 44 } 45 return 0; 46 }
C. Trailing Loves (or L'oeufs?)
Solved.
题意:
求$n!在b进制下末尾的0的个数$
思路:
我们考虑什么时候会产生$0$
对于$b进制,我们知道满b进1,末尾添一个0$
$那么我们只需要知道n!中所有数因数分解之后可以凑成多少个b末尾就是多少个0$
我们令$b = p_1^{t_1} \cdot p_2^{t_2} \cdots p_n^{t_n}$
那么求出$n!中有多少个 p_1^{t_1}, p_2^{t_2} \cdots p_n^{t_n} 取Min即可$
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll unsigned long long 5 #define pll pair <ll, ll> 6 ll n, b; 7 8 ll f() 9 { 10 ll res = (ll)1e18; 11 ll limit = sqrt(b); 12 vector <pll> vec; 13 for (ll i = 2; i <= limit; ++i) if (b % i == 0) 14 { 15 pll tmp = pll(i, 0); 16 while (b % i == 0) 17 { 18 ++tmp.second; 19 b /= i; 20 } 21 vec.push_back(tmp); 22 } 23 if (b != 1) 24 vec.push_back(pll(b, 1)); 25 for (auto it : vec) 26 { 27 ll tmp = 0; 28 for (ll i = it.first; ; i *= it.first) 29 { 30 tmp += n / i; 31 if (i > n / it.first + 10) break; 32 } 33 res = min(res, tmp / it.second); 34 } 35 return res; 36 } 37 38 int main() 39 { 40 while (scanf("%llu%llu", &n, &b) != EOF) 41 { 42 printf("%llu\n", f()); 43 } 44 return 0; 45 }
D. Flood Fill
Upsolved.
题意:
$有一行不同颜色的球$
$首先可以选择一个主球,相同的球会合并$
$在接下来的每一步,我们都可以规定主球为任一颜色$
$和它相邻的并且颜色和它相同的都会合并$
$求所有球合并完的最少步骤$
思路:
$DP解法$
$dp[l][r][0/1] 表示 l->r 这个区间合并完后状态为0/1的最小步数$
$0表示合并后的颜色和c[l]相同,1表示合并后的颜色和c[1]相同$
$那么dp[l][r][0/1] 可以转移到 dp[l - 1][r][0]$
$dp[l][r][0/1] 可以转移到 dp[l][r + 1][1]$
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 5010 5 int n, c[N]; 6 int f[N][N][2]; 7 8 int dp(int l, int r, int sta) 9 { 10 if (l >= r) 11 return 0; 12 if (f[l][r][sta] != -1) 13 return f[l][r][sta]; 14 int res = (int)1e8; 15 if (sta == 0) 16 { 17 res = min(res, dp(l + 1, r, 0) + (c[l] != c[l + 1])); 18 res = min(res, dp(l + 1, r, 1) + (c[l] != c[r])); 19 } 20 else 21 { 22 res = min(res, dp(l, r - 1, 0) + (c[r] != c[l])); 23 res = min(res, dp(l, r - 1, 1) + (c[r] != c[r - 1])); 24 } 25 return f[l][r][sta] = res; 26 } 27 28 int main() 29 { 30 while (scanf("%d", &n) != EOF) 31 { 32 for (int i = 1; i <= n; ++i) scanf("%d", c + i); 33 memset(f, -1, sizeof f); 34 printf("%d\n", min(dp(1, n, 0), dp(1, n, 1))); 35 } 36 return 0; 37 }
$LCS解法$
我们假设$主球的位置在x$
$那么x把区间分成左右两部分$
$每次可以选择左边一个合并或者右边一个合并$
$但是更优的是 左边和右边的相同,那么一步就可以将他们都合并$
$我们就是要找这样相同的$
假如这样的东西
$abcxcba$
$x代表主球,相同字母代表相同数字$
$那么我们只需要三次就可以合并完$
$我们要找的就是这种相同的越多越好,并且是有顺序可以接在一起的$
$那么把字符串翻转求LCS就可以$
$要注意除2 ,因为多算了一次$
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 5010 5 int n, m, a[N], b[N]; 6 int f[N][N]; 7 8 int main() 9 { 10 while (scanf("%d", &n) != EOF) 11 { 12 a[0] = -1; m = 0; 13 for (int i = 1, x; i <= n; ++i) 14 { 15 scanf("%d", &x); 16 if (x != a[m]) a[++m] = x; 17 } 18 for (int i = 1; i <= m; ++i) 19 b[m - i + 1] = a[i]; 20 memset(f, 0, sizeof f); 21 for (int i = 1; i <= m; ++i) 22 for (int j = 1; j <= m; ++j) 23 { 24 f[i][j] = max(f[i][j - 1], f[i - 1][j]); 25 if (a[i] == b[j]) 26 f[i][j] = max(f[i][j], f[i - 1][j - 1] + 1); 27 } 28 printf("%d\n", m - 1 - f[m][m] / 2); 29 } 30 return 0; 31 }
下面这个线段树版本本质上和$LCS思想差不多$
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 10010 5 int n, m, c[N]; 6 vector <int> vec[N]; 7 8 namespace SEG 9 { 10 int Max[N << 2]; 11 void init(){ memset(Max, 0, sizeof Max); } 12 void update(int id, int l, int r, int pos, int val) 13 { 14 if (l == r) 15 { 16 Max[id] = max(Max[id], val); 17 return; 18 } 19 int mid = (l + r) >> 1; 20 if (pos <= mid) update(id << 1, l, mid, pos, val); 21 else update(id << 1 | 1, mid + 1, r, pos, val); 22 Max[id] = max(Max[id << 1], Max[id << 1 | 1]); 23 } 24 int query(int id, int l, int r, int ql, int qr) 25 { 26 if (qr < ql) return 0; 27 if (l >= ql && r <= qr) return Max[id]; 28 int res = 0; 29 int mid = (l + r) >> 1; 30 if (ql <= mid) res = max(res, query(id << 1, l, mid, ql, qr)); 31 if (qr > mid) res = max(res, query(id << 1 | 1, mid + 1, r, ql, qr)); 32 return res; 33 } 34 } 35 36 int main() 37 { 38 while (scanf("%d", &n) != EOF) 39 { 40 m = 0, c[0] = -1; 41 for (int i = 1, x; i <= n; ++i) 42 { 43 scanf("%d", &x); 44 if (x != c[m]) c[++m] = x; 45 } 46 for (int i = m + 1; i <= 2 * m; ++i) c[i] = c[i - m]; 47 for (int i = 1; i <= m; ++i) vec[i].clear(); 48 for (int i = 2 * m; i >= m + 1; --i) vec[c[i]].push_back(i); 49 int res = (int)1e8; 50 SEG::init(); 51 for (int i = m; i >= 1; --i) 52 { 53 for (auto it : vec[c[i]]) 54 { 55 int tmp = SEG::query(1, 1, 2 * m, m + 1, it - 1); 56 SEG::update(1, 1, 2 * m, it, tmp + 1); 57 } 58 } 59 for (int i = m; i <= 2 * m; ++i) res = min(res, m - 1 - SEG::query(1, 1, 2 * m, i, i) / 2); 60 printf("%d\n", res); 61 } 62 return 0; 63 }
E. Arithmetic Progression
Upsolved.
题意:
给出一个等差序列,但是不有序
$有两种询问方式$
$?\; i 表示询问第i个位置上的数是多少$
$> x 表示询问序列中存不存在严格大于x的数字$
最后给出首项和公差
询问次数最多60
思路:
首先可以用$30次询问求出上界$
$接着如果搞定公差下界就有了$
$我们随机询问30个数,就得到900个差值$
$再求这个差值的gcd就是答案$
$为什么不会出现求的值的答案的倍数的情况?$
$概率很小,官方题解有证 (逃$
注意随机的时候要用大数随机
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 #define N 1000010 6 int n, cnt; 7 8 bool check(int x) 9 { 10 printf("> %d\n", x); 11 ++cnt; 12 fflush(stdout); 13 int tmp; 14 scanf("%d", &tmp); 15 return tmp == 0; 16 } 17 18 int vis[N]; 19 int gcd(int a, int b) 20 { 21 return b ? gcd(b, a % b) : a; 22 } 23 24 int main() 25 { 26 while (scanf("%d", &n) != EOF) 27 { 28 if (n <= 60) 29 { 30 vector <int> vec; 31 for (int i = 1, x; i <= n; ++i) 32 { 33 printf("? %d\n", i); 34 fflush(stdout); 35 scanf("%d", &x); 36 vec.push_back(x); 37 } 38 sort(vec.begin(), vec.end()); 39 printf("! %d %d\n", vec[0], vec[1] - vec[0]); 40 fflush(stdout); 41 continue; 42 } 43 cnt = 0; 44 int l = 0, r = (int)1e9, res = -1; 45 while (r - l >= 0) 46 { 47 int mid = (l + r) >> 1; 48 if (check(mid)) 49 { 50 res = mid; 51 r = mid - 1; 52 } 53 else 54 l = mid + 1; 55 } 56 vector <int> vec, gap; 57 vec.push_back(res); 58 memset(vis, 0, sizeof vis); 59 for (int i = cnt + 1, x; i <= 60; ++i) 60 { 61 do 62 { 63 x = ((rand() << 14) + rand()) % n + 1; 64 } while (vis[x]); 65 vis[x] = 1; 66 printf("? %d\n", x); 67 fflush(stdout); 68 scanf("%d", &x); 69 vec.push_back(x); 70 } 71 sort(vec.begin(), vec.end()); 72 vec.erase(unique(vec.begin(), vec.end()), vec.end()); 73 for (int i = 0, len = vec.size(); i < len; ++i) 74 for (int j = i + 1; j < len; ++j) 75 gap.push_back(vec[j] - vec[i]); 76 int d = 0; 77 for (auto it : gap) d = gcd(d, it); 78 printf("! %d %d\n", res - (n - 1) * d, d); 79 fflush(stdout); 80 } 81 return 0; 82 }
F. Please, another Queries on Array?
Upsolved.
题意:
给出一个序列
有两种操作
$M\; l, r, x 将[l, r]里面的数乘上x$
$q\; l, r 询问\phi(\prod_{i = l}^{r} a_i) mod 10^9 + 7$
思路:
考虑$\phi(p^k) = p^{k - 1} \cdot (p - 1)$
并且$\phi()是积性函数$
$有\phi(x \cdot y) = \phi(x) \cdot \phi(y)$
$那么令k = p_1^{t_1} \cdot p_2^{t_2} \cdots p_n^{t_n}$
那么
$\phi(k) = \phi(p_1^{t_1}) \cdots$
$\phi(k) = p_1^{t_1 - 1} \cdot (p_1 - 1) \cdots$
$\phi(k) = p_1^{t_1} \cdot \frac{p_1 - 1}{p_1} \cdots$
那么询问的答案就是
$\phi(k) = k \cdot \prod_{p \in P} \frac{p - 1}{p}$
前面那一段用乘积线段树维护
考虑题目的数的值中涉及到的素数只有62个
$后面那一段用或线段树维护是否存在即可$
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 #define N 400010 6 const ll MOD = (ll)1e9 + 7; 7 int n, m, q, a[N]; 8 int prime[110], pos[310]; 9 ll inv[110], b[310]; 10 11 void init() 12 { 13 m = 0; 14 memset(pos, 0, sizeof pos); 15 for (int i = 2; i <= 300; ++i) 16 { 17 if (!pos[i]) 18 prime[++m] = i; 19 for (int j = i * i; j <= 300; j += i) 20 pos[j] = 1; 21 } 22 for (int i = 1; i <= 300; ++i) 23 { 24 b[i] = 0; 25 for (int j = 1; j <= 62; ++j) 26 if (i % prime[j] == 0) 27 b[i] |= (1ll << (j - 1)); 28 } 29 } 30 31 ll qmod(ll base, ll n) 32 { 33 ll res = 1; 34 while (n) 35 { 36 if (n & 1) res = res * base % MOD; 37 base = base * base % MOD; 38 n >>= 1; 39 } 40 return res; 41 } 42 43 namespace SEG 44 { 45 struct node 46 { 47 int cnt; 48 ll sum, lazy; 49 ll S, W; 50 void init() 51 { 52 sum = 1; 53 lazy = 1; 54 S = 0, W = 0; 55 } 56 void add(ll val, ll SE) 57 { 58 sum = (sum * qmod(val, cnt)) % MOD; 59 lazy = (lazy * val) % MOD; 60 S |= SE; 61 W |= SE; 62 } 63 node operator + (const node &other) const 64 { 65 node res; res.init(); 66 res.sum = (sum * other.sum) % MOD; 67 res.S = S | other.S; 68 res.cnt = cnt + other.cnt; 69 return res; 70 } 71 }a[N << 2], res; 72 void pushdown(int id) 73 { 74 if (a[id].lazy == 1 && a[id].W == 0) return; 75 a[id << 1].add(a[id].lazy, a[id].W); 76 a[id << 1 | 1].add(a[id].lazy, a[id].W); 77 a[id].lazy = 1; 78 a[id].W = 0; 79 } 80 void build(int id, int l, int r) 81 { 82 a[id].init(); 83 a[id].cnt = (r - l + 1); 84 if (l == r) return; 85 int mid = (l + r) >> 1; 86 build(id << 1, l, mid); 87 build(id << 1 | 1, mid + 1, r); 88 } 89 void update(int id, int l, int r, int ql, int qr, ll val, ll SE) 90 { 91 if (l >= ql && r <= qr) 92 { 93 a[id].add(val, SE); 94 return; 95 } 96 int mid = (l + r) >> 1; 97 pushdown(id); 98 if (ql <= mid) update(id << 1, l, mid, ql, qr, val, SE); 99 if (qr > mid) update(id << 1 | 1, mid + 1, r, ql, qr, val, SE); 100 a[id] = a[id << 1] + a[id << 1 | 1]; 101 } 102 void query(int id, int l, int r, int ql, int qr) 103 { 104 if (l >= ql && r <= qr) 105 { 106 res = res + a[id]; 107 return; 108 } 109 int mid = (l + r) >> 1; 110 pushdown(id); 111 if (ql <= mid) query(id << 1, l, mid, ql, qr); 112 if (qr > mid) query(id << 1 | 1, mid + 1, r, ql, qr); 113 } 114 } 115 116 int main() 117 { 118 init(); 119 for (int i = 1; i <= 62; ++i) 120 inv[i] = qmod(prime[i], MOD - 2); 121 while (scanf("%d%d", &n, &q) != EOF) 122 { 123 for (int i = 1; i <= n; ++i) scanf("%d", a + i); 124 SEG::build(1, 1, n); 125 for (int i = 1; i <= n; ++i) 126 SEG::update(1, 1, n, i, i, a[i], b[a[i]]); 127 char op[20]; int l, r, x; 128 for (int i = 1; i <= q; ++i) 129 { 130 scanf("%s%d%d", op, &l, &r); 131 if (op[0] == 'M') 132 { 133 scanf("%d", &x); 134 SEG::update(1, 1, n, l, r, x, b[x]); 135 } 136 else 137 { 138 SEG::res.init(); 139 SEG::query(1, 1, n, l, r); 140 ll res = SEG::res.sum; 141 ll S = SEG::res.S; 142 for (int j = 1; j <= 62; ++j) 143 if ((S >> (j - 1)) & 1ll) 144 res = (res * (prime[j] - 1) % MOD * inv[j]) % MOD; 145 printf("%lld\n", res); 146 } 147 } 148 } 149 return 0; 150 }