Solved | A | B | C | D | E | F | G | H | I | J | K |
---|---|---|---|---|---|---|---|---|---|---|---|
4/10 | Ø | . | . | Ø | . | . | Ø | . | . | Ø | . |
題意:
白雲每次能夠移動\(1\)米或者\(k\)米,詢問移動的米數在\([L, R]\)範圍內的方案數有多少。git
思路:
\(dp[i][0/1]\)表示到第\(i\)米,是經過\(1\)米的方式過來的仍是\(k\)米的方式過來的,遞推便可。數組
代碼:dom
#include <bits/stdc++.h> using namespace std; #define N 100010 const int p = 1e9 + 7; int f[N][2], g[N]; int q, k, l, r; void add(int &x, int y) { x += y; if (x >= p) { x -= p; } } int main() { scanf("%d%d", &q, &k); memset(f, 0, sizeof f); f[0][0] = 1; for (int i = 0; i <= 100000; ++i) { add(f[i + 1][0], (f[i][0] + f[i][1]) % p); if (i + k <= 100000) { add(f[i + k][1], f[i][0]); } } memset(g, 0, sizeof g); for (int i = 1; i <= 100000; ++i) { g[i] = g[i - 1]; add(g[i], f[i][0]); add(g[i], f[i][1]); } while (q--) { scanf("%d%d", &l, &r); printf("%d\n", (g[r] - g[l - 1] + p) % p); } return 0; }
題意:
有\(n\)個物品,從\(1\)到\(n\)的順序去訪問,身上最多隻能攜帶一個物品,每次能夠買進或者賣出物品,身上有無限的錢,問最後得到的利潤最可能是多少。spa
思路:
考慮將買入和賣出合併成一種操做,買入就是減去收益,賣出是增長收益,維護兩個堆,遍歷\(i\)個物品。指針
代碼:code
#include <bits/stdc++.h> using namespace std; #define ll long long #define N 100010 int n, a[N]; struct node { ll tot; int cnt; node() {} node (ll tot, int cnt) : tot(tot), cnt(cnt) {} bool operator < (const node &other) const { if (tot == other.tot) { return cnt > other.cnt; } return tot < other.tot; } }; int main() { int T; scanf("%d", &T); while (T--) { scanf("%d", &n); for (int i = 1; i <= n; ++i) { scanf("%d", a + i); } //0表示上一次操做是買入 //1表示上一次操做是賣出 priority_queue <node> pq[2]; node res = node(0, 0); pq[0].push(node(-a[1], 1)); node t1, t2; for (int i = 2; i <= n; ++i) { pq[0].push(node(-a[i], 1)); if (!pq[0].empty()) { t1 = pq[0].top(); pq[1].push(node(t1.tot + a[i], t1.cnt + 1)); } if (!pq[1].empty()) { t2 = pq[1].top(); pq[0].push(node(t2.tot - a[i], t2.cnt + 1)); } if (!pq[1].empty()) { res = max(res, pq[1].top()); } } printf("%lld %d\n", res.tot, res.cnt); } return 0; }
題意:
一維座標系上,在\(x_i\)有\(a_i\)個物品,每次移動一個物品的代價爲\(2 \cdot abs(x_u - x_v)\),如今有\(T\)元錢,問在不超過\(T\)的代價下,移動物品使得
在同一個位置上的物品最多。orm
思路:
顯然能夠二分物品數量,check的時候枚舉左端點,雙指針維護右端點,多餘的部分從右端回退。
再枚舉右端點,反着搞一遍。get
代碼:
#include <bits/stdc++.h> using namespace std; #define ll long long #define N 500010 int n, x[N], a[N]; ll sum[N], cost[N]; ll T, L, R, need; int u; ll cost_l(int i) { //將L+1~i的物品移動到i的費用 ll a = (sum[i] - sum[L]) * x[i] - (cost[i] - cost[L]); //將i+1~R的物品移動到i的費用 ll b = (cost[R] - cost[i]) - x[i] * (sum[R] - sum[i]); //將多餘的物品從R移動到i的費用 ll c = (sum[R] - sum[L] - need) * (x[R] - x[i]); return a + b - c; } ll cost_r(int i) { //將L+1~i的物品移動到i的費用 ll a = (sum[i] - sum[L]) * x[i] - (cost[i] - cost[L]); //將i+1~R的物品移動到i的費用 ll b = (cost[R] - cost[i]) - x[i] * (sum[R] - sum[i]); //將多餘的物品從L移動到i的費用 ll c = (sum[R] - sum[L] - need) * (x[i] - x[L + 1]); return a + b - c; } bool check(ll x) { need = x; L = 0, R = 1, u = 0; while (1) { while (R < n && sum[R] - sum[L] < x) ++R; if (sum[R] - sum[L] < x) break; while (u < L) ++u; while (u < R && cost_l(u) > cost_l(u + 1)) ++u; if (cost_l(u) <= T) return 1; ++L; } L = n - 1, R = n, u = n; while (1) { while (L > 0 && sum[R] - sum[L] < x) --L; if (sum[R] - sum[L] < x) break; while (u > R) --u; while (u > L && cost_r(u) > cost_r(u - 1)) --u; if (cost_r(u) <= T) return 1; --R; } return 0; } int main() { while (scanf("%d%lld", &n, &T) != EOF) { T /= 2; for (int i = 1; i <= n; ++i) { scanf("%d", x + i); } for (int i = 1; i <= n; ++i) { scanf("%d", a + i); sum[i] = sum[i - 1] + a[i]; cost[i] = cost[i - 1] + 1ll * a[i] * x[i]; } ll l = 0, r = sum[n], res = -1; while (r - l >= 0) { ll mid = (l + r) >> 1; if (check(mid)) { l = mid + 1; res = mid; } else { r = mid - 1; } } printf("%lld\n", res); } return 0; }
題意:
在\(n \cdot m\)的農田上,有\(n \cdot m\)棵植物,每棵植物只能施放第\(a[i][j]\)種肥料,有\(T\)次操做,每次操做時將\(x_1, y_1, x_2, y_2\)矩形內的做物都施上第\(k_i\)種肥料,
一旦做物被施上不是第\(a[i][j]\)種肥料,它就會馬上死亡。
問最後死亡的做物數目.
思路一:
考慮:
做物的施肥次數 = 第\(a[i][j]\)種肥料的施肥次數+其餘種類肥料的施肥次數。
咱們先二維差分求出全部做物的總的施肥次數。
而後將操做按\(k_i\)分類,用二維BIT維護二維前綴和,表示\(k_i\)操做下做物的施肥次數。
而後再枚舉初始值爲\(k_i\)的全部做物,判斷它總的施肥次數以及第\(k_i\)種肥料的施肥次數是否相等,不相等就掛了。
時間複雜度:\(\mathcal{O}(nm + T \cdot log(n) \cdot log(m))\)
代碼一:
#include <bits/stdc++.h> using namespace std; #define N 1000010 #define pii pair <int, int> #define fi first #define se second int n, m, q; struct node { int x[2], y[2]; node() {} node(int x1, int y1, int x2, int y2) { x[0] = x1; x[1] = x2; y[0] = y1; y[1] = y2; } }; vector < vector <pii> > a; vector < vector <node> > b; struct BIT { vector < vector <int> > a; void init() { a.clear(); a.resize(n + 1); for (int i = 0; i < n + 1; ++i) { a[i].resize(m + 1); } } void update(int x, int y, int v) { for (int i = x; i <= n; i += i & -i) { for (int j = y; j <= m; j += j & -j) { a[i][j] += v; } } } void update(int x1, int y1, int x2, int y2, int v) { update(x1, y1, v); update(x2 + 1, y2 + 1, v); update(x1, y2 + 1, -v); update(x2 + 1, y1, -v); } int query(int x, int y) { int res = 0; for (int i = x; i > 0; i -= i & -i) { for (int j = y; j > 0; j -= j & -j) { res += a[i][j]; } } return res; } }bit; void read(int &x) { x = 0; char ch; while (!isdigit(ch = getchar())); while (isdigit(ch)) { x = x * 10 + ch - '0'; ch = getchar(); } } int main() { while (scanf("%d%d%d", &n, &m, &q) != EOF) { a.clear(); a.resize(n * m + 1); b.clear(); b.resize(n * m + 1); bit.init(); for (int i = 1; i <= n; ++i) { for (int j = 1, x; j <= m; ++j) { read(x); a[x].emplace_back(i, j); } } for (int i = 1, k, x1, y1, x2, y2; i <= q; ++i) { read(x1); read(y1); read(x2); read(y2); read(k); b[k].push_back(node(x1, y1, x2, y2)); bit.update(x1, y1, x2, y2, 1); } int res = 0; for (int i = 1; i <= n * m; ++i) { for (auto it : b[i]) { bit.update(it.x[0], it.y[0], it.x[1], it.y[1], -1); } for (auto it : a[i]) { if (bit.query(it.fi, it.se) != 0) { ++res; } } for (auto it : b[i]) { bit.update(it.x[0], it.y[0], it.x[1], it.y[1], 1); } } printf("%d\n", res); } return 0; }
思路二:
考慮每次施肥的時候加上的是\(k_i\)而不是1,那麼最終若是做物沒有死,那麼它的值應該是\(a[i][j] \cdot 施肥次數\)。
可是這樣容易被卡,將權值映射成素數便可。
時間複雜度:\(\mathcal{O}(n + m + 10^7)\)
代碼二:
#include <bits/stdc++.h> using namespace std; #define ll long long #define N 15500010 int prime[1000010], tot; bool check[N]; void init() { tot = 0; memset(check, 0, sizeof check); for (int i = 2; i < N; ++i) { if (!check[i]) { prime[++tot] = i; if (tot >= 1000000) break; } for (int j = 1; j <= tot; ++j) { if (1ll * i * prime[j] >= N) break; check[i * prime[j]] = 1; if (i % prime[j] == 0) { break; } } } } int n, m, q; vector <vector<int>> a, c; vector <vector<ll>> b; template <class T> void up(vector <vector<T>> &vec, int x1, int y1, int x2, int y2, int v) { vec[x1][y1] += v; vec[x2 + 1][y2 + 1] += v; vec[x1][y2 + 1] -= v; vec[x2 + 1][y1] -= v; } template <class T> void work(vector <vector<T>> &vec) { for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { vec[i][j] += vec[i - 1][j] + vec[i][j - 1] - vec[i - 1][j - 1]; } } } int main() { init(); random_shuffle(prime + 1, prime + 1 + tot); while (scanf("%d%d%d", &n, &m, &q) != EOF) { a.clear(); a.resize(n + 2, vector <int> (m + 2, 0)); b.clear(); b.resize(n + 2, vector <ll> (m + 2, 0)); c.clear(); c.resize(n + 2, vector <int> (m + 2, 0)); for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { scanf("%d", &a[i][j]); } } int x[2], y[2], k; while (q--) { scanf("%d%d%d%d%d", x, y, x + 1, y + 1, &k); up(b, x[0], y[0], x[1], y[1], prime[k]); up(c, x[0], y[0], x[1], y[1], 1); } work(b); work(c); int res = 0; for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { if (b[i][j] != c[i][j] * prime[a[i][j]]) { ++res; } } } printf("%d\n", res); } return 0; }
思路三:
依據:
\[ \begin{eqnarray*} 2c &=& a + b\\ 2c^2 &=& a^2 + b^2 \\ \end{eqnarray*} \]
當且僅當\(a = b = c\)時成立。
增長一個平方驗證。
代碼三:
#include <bits/stdc++.h> using namespace std; #define ll long long int n, m, q; vector <vector<int>> a, c; vector <vector<ll>> b; template <class T> void up(vector <vector<T>> &vec, int x1, int y1, int x2, int y2, int v) { vec[x1][y1] += v; vec[x2 + 1][y2 + 1] += v; vec[x1][y2 + 1] -= v; vec[x2 + 1][y1] -= v; } template <class T> void work(vector <vector<T>> &vec) { for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { vec[i][j] += vec[i - 1][j] + vec[i][j - 1] - vec[i - 1][j - 1]; } } } void read(int &x) { x = 0; char ch; while (!isdigit(ch = getchar())); while (isdigit(ch)) { x = x * 10 + ch - '0'; ch = getchar(); } } int main() { while (scanf("%d%d%d", &n, &m, &q) != EOF) { a.clear(); a.resize(n + 2, v ector <int> (m + 2, 0)); b.clear(); b.resize(n + 2, vector <ll> (m + 2, 0)); c.clear(); c.resize(n + 2, vector <int> (m + 2, 0)); for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { read(a[i][j]); } } int x[2], y[2], k; while (q--) { read(x[0]); read(y[0]); read(x[1]); read(y[1]); read(k); up(b, x[0], y[0], x[1], y[1], 1ll * k); up(c, x[0], y[0], x[1], y[1], 1); } work(b); work(c); int res = 0; for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { if (b[i][j] != c[i][j] * a[i][j]) { ++res; } } } printf("%d\n", res); } return 0; }
思路四:
考慮兩個數不一樣,那麼它們的二進制位至少有一位是不一樣的。
那麼考慮枚舉二進制位,當每次操做的\(k_i\)的當前二進制上爲一時那麼施肥。
而後枚舉每一個做物:
實現的時候開一維數組而後映射二維座標就過了,開二維vector就T了。。
代碼四:
#include <bits/stdc++.h> using namespace std; #define N 4000010 #define y1 y_1 int x1[N], y1[N], x2[N], y2[N], k[N]; int n, m, q; int a[N], b[N], c[N]; bool die[N]; int id(int x, int y) { return (x - 1) * (m + 2) + y; } void up(int *f, int x1, int y1, int x2, int y2, int v) { f[id(x1, y1)] += v; f[id(x2 + 1, y2 + 1)] += v; f[id(x1, y2 + 1)] -= v; f[id(x2 + 1, y1)] -= v; } void work(int *f) { for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { f[id(i, j)] += f[id(i - 1, j)] + f[id(i, j - 1)] - f[id(i - 1, j - 1)]; } } } int main() { while (scanf("%d%d%d", &n, &m, &q) != EOF) { memset(b, 0, sizeof b); for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { scanf("%d", &a[id(i, j)]); } } for (int i = 1; i <= q; ++i) { scanf("%d%d%d%d%d", x1 + i, y1 + i, x2 + i, y2 + i, k + i); up(b, x1[i], y1[i], x2[i], y2[i], 1); } work(b); for (int i = 15; i >= 0; --i) { for (int _i = 1; _i <= n; ++_i) { for (int _j = 1; _j <= m; ++_j) { c[id(_i, _j)] = 0; } } for (int j = 1; j <= q; ++j) { if (k[j] >> i & 1) { up(c, x1[j], y1[j], x2[j], y2[j], 1); } } work(c); for (int _i = 1; _i <= n; ++_i) { for (int _j = 1; _j <= m; ++_j) { if (a[id(_i, _j)] >> i & 1) { if (b[id(_i, _j)] != c[id(_i, _j)]) { die[id(_i, _j)] = 1; } } else if (c[id(_i, _j)]) { die[id(_i, _j)] = 1; } } } } int res = 0; for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { res += die[id(i, j)]; } } printf("%d\n", res); } return 0; }