1001
数学 Lucas定理
解题思路
一看题就知道我们要求当 \(b_i \in [1, L_i]\) 内,有多少 \(b_i\) 使得 \((^{a_i}_{b_i})\) 是奇数。
于是我们看 \(Lucas\) 定理:
对于素数 \(p\),有:
\[(^n_k) \equiv (^{\lfloor{n \over p}\rfloor}_{\lfloor{k \over p}\rfloor})(^{n \% p}_{k \% p}) (mod\ p) \]其中当 \(n < k\) 时, \((^n_k) = 0\)。
当我们将 2 代入 \(p\),我们还可以这样看 \(lucas\) 定理:
其中 \(x >> 1\) 就是 \(\lfloor {x \over 2} \rfloor\)。
当 \(p\) 等于 2 时,我们可以发现 \((^0_1) = 0\)。这就意味着若想 \((^n_k)\) 是奇数,则不能出现 \(n\) 是偶数时且 \(k\) 是奇数这种情况。推广到每一位,就是当 \(n\) 的某一位是 0 时, \(k\) 中对应的那一位不能是 1。
于是符合条件的 \(b_i\) 就显然了:既要满足在给定的范围之内,又要满足由 \(Lucas\) 定理推导出来的限制。
CODE
int a[N + 5];int cnt(int L, int x) {// 不大于 L 的最大的满足 x 的数int cur = 0, res = 0;for (int i = 30; i >= 0; i--) {if (x >> i & 1) {res <<= 1;if ((cur | (1 << i)) <= L){cur |= (1 << i);res |= 1;}}}return res + 1;
}void solve()
{int n = 0;std::cin >> n;for (int i = 0; i < n; i++) {std::cin >> a[i];}i64 ans = 1ll;for (int i = 0, L = 0; i < n; i++) {std::cin >> L;(ans *= cnt(L, a[i])) %= Mod;}std::cout << ans << '\n';
}
1003
思维 数据结构
解题思路
先将靠初始值就满足条件的公司都放在一个队列里,对于初始值不满足条件的公司,我们记录下有多少项不满足条件,并且对于每一项不满足条件的能力,我们用一个小根堆维护(后面能力提升后就可以快速找到满足了那些条件)
CODE
i64 a[M], cnt[N]; // cnt[i] 有多少能力不满组第 i 各公司的要求bool solve()
{int n = 0, m = 0;std::cin >> n >> m;for (int i = 0; i < m; i++) {std::cin >> a[i];}std::vector w(n, std::vector(m, 0));std::vector pq(m, std::priority_queue<std::pair<int, int>>{});std::queue<int> q;for (int i = 0; i < n; i++) {cnt[i] = 0;for (int j = 0; j < m; j++) {int c = 0;std::cin >> c;if (a[j] < c) {cnt[i]++;pq[j].push({ -c, i });}}if (cnt[i] == 0) {// 初始时就满足的直接入队q.push(i);}for (int j = 0; j < m; j++) {std::cin >> w[i][j];}}while (not q.empty()) {int cur = q.front();q.pop();for (int i = 0; i < m; i++) {a[i] += w[cur][i];while (not pq[i].empty() && -(pq[i].top().first) <= a[i]) {auto [val, idx] = pq[i].top();pq[i].pop();if ((--cnt[idx]) == 0) {// 全部满足了就入队q.push(idx);}}}}for (int i = 0; i < n; i++) {if (cnt[i] != 0) {return false;}}return true;
}
1004
动态规划
解题思路
首先我们将筷子按升序排号,此时对于大部分的筷子,最优应该和相邻的配对,特殊情况是:当两个必须要拿的筷子之间隔了一双筷子,我们就要看这两个必须要拿的筷子是配成一双还是各自找其他的配对。
转移直接看代码就好了,特别要注意初始化。
CODE
std::pair<int, bool> c[N + 5];
i64 f[N + 5][2];void solve()
{int n = 0, m = 0;std::cin >> n >> m;for (int i = 1; i <= n; i++) {std::cin >> c[i].first;c[i].second = false;}for (int i = 0; i < m; i++) {int a = 0;std::cin >> a;c[a].second = true;}std::sort(c + 1, c + 1 + n);// 这些都是不存在的情况。f[0][0] = f[0][1] = f[1][1] = Inf;for (int i = 2; i <= n; i++) {if (c[i - 1].second) { // 第 i - 1 只必须拿f[i][0] = f[i - 1][1];f[i][1] = f[i - 1][0] + pow(c[i].first - c[i - 1].first, 2);}else {f[i][0] = std::min(f[i - 1][0], f[i - 1][1]);f[i][1] = std::min(f[i - 1][0] + i64(pow(c[i].first - c[i - 1].first, 2)), i64(f[i - 2][0] + pow(c[i].first - c[i - 2].first, 2)));}}std::cout << std::min(f[n][1], (c[n].second ? Inf : f[n][0])) << '\n';return;
}
1005
并查集
解题思路
并查集秒了
1007
可持久化字典树(明天再学)
1008
概率 扫描线
解题思路
要是抛开这题的期望,就单纯的求给定的区间内有多少不同的数,这就是一个比较板子的扫描线问题:模板题。实际上,本题的解法和思维难度就类似于模板题的 pro plus
假设现在我们有一段长度为 \(len\) 的区间,我们要计算在这个区间内随机选择一个的区间中包含的数的个数(包括相同的数)的期望。对于这个问题,答案的分母就是 \(len (len + 1) \over 2\),分母是 \(\sum_{i = 1}^{len}(i(len + 1 - i)) = {len(len + 1)(len + 2) \over 6}\)。
若我们要求区间中包含的不同的数的个数的期望要怎么做呢?我们就减去分子中那些重复的数字的贡献就好了。具体的,我们令 \(l(r)\) 为小于 \(r\) 的最大位置使得 \(a[l(r)] = a[r]\),于是若 \(l[r]\) 和 \(r\) 都在区间 \([L, R]\) 内,我们就认为要减去 \(a[r]\) 对分子的贡献:\((l[r] - L + 1)(R - r + 1) = (r - 1)L + (l(r) + 1)R -LR - l(r)r + l(r) - r + 1\),可以发现,\(r\) 决定的是展开式中各项的系数。这是单个位置 \(r\) 需要减去的贡献,对于所有需要减去贡献的位置的总贡献,我们只需要统计各项系数的和再将具体的 \(L\) 和 \(R\) 代入就好了。
CODE
constexpr int N = 2e5, X = 2e5;class BIT {
private:int n;std::vector<i64> tr;int lowbit(int x) {return x & -x;}i64 sum(int l) {i64 res = 0;while (l >= 1) {res += tr[l];l -= lowbit(l);}return res;}
public:BIT(int _n) : n(_n) {tr.assign(n + 1, 0ll);}void add(int pos, i64 val) {while (pos <= n) {tr[pos] += val;pos += lowbit(pos);}}i64 sum(int l, int r) {if (l > r) {return 0;}else {return (sum(r) - sum(l - 1)) % Mod;}}
};i64 qpow(i64 a, i64 p) {i64 res = 1;while (p) {if (p & 1) {(res *= a) %= Mod;}(a *= a) %= Mod;p >>= 1;}return res;
}
i64 inv(i64 a) {return qpow(a, Mod - 2);
}int a[N + 5], lst[N + 5], pos[N + 5];
i64 I[N + 5], A[N + 5];
i64 ans[N + 5];
std::vector qr(N + 5, std::vector<std::array<int, 2>>{});void solve()
{int n = 0, q = 0;std::cin >> n >> q;std::fill(pos + 1, pos + 1 + n, 0);for (int i = 1; i <= n; i++) {qr[i].clear();std::cin >> a[i];lst[i] = pos[a[i]];pos[a[i]] = i;}for (int i = 0; i < q; i++) {int l = 0, r = 0;std::cin >> l >> r;qr[r].push_back({ l, i });}BIT L(n), R(n), LR(n), C(n);for (int r = 1; r <= n; r++) {if (lst[r] != 0) {L.add(lst[r], r - 1);R.add(lst[r], lst[r] + 1);LR.add(lst[r], -1);C.add(lst[r], -1ll * lst[r] * r + lst[r] - r + 1);}for (auto &[l, idx] : qr[r]) {ans[idx] = A[r - l + 1];(ans[idx] += Mod - (L.sum(l, r) * l + R.sum(l, r) * r + LR.sum(l, r) * l * r + C.sum(l, r)) % Mod) %= Mod;(ans[idx] *= I[r - l + 1]) %= Mod;}}for (int i = 0; i < q; i++) {std::cout << ans[i] << '\n';}
}int main()
{IOS;int _t = 1;std::cin >> _t;for (int i = 1; i <= N; i++) {I[i] = inv((1ll * (i + 1) * i / 2ll) % Mod); // 预处理分母A[i] = (1ll * i * (i + 1) * (i + 2) / 6ll) % Mod; // 预处理分子}while (_t--){solve();}system("pause");return 0;
}
1009
并查集
解题思路
最主要的一点就是,交换部落时,我们不真的去交换部落中所有的人,而是交换两个部落之间的编号(我们称之为实际编号),这就要求我们建立起实际编号与题目所给编号(我们称之为原始编号)之间的双射关系。
CODE
int fa[N + 5], pos[N + 5], idx[N + 5], g[N + 5];
int getfa(int u) {return u == fa[u] ? u : (fa[u] = getfa(fa[u]));
}
// u <- v;
void merge(int u, int v) {fa[getfa(v)] = getfa(u);;
}void solve()
{int n = 0, q = 0;std::cin >> n >> q;std::iota(fa + 1, fa + 1 + n, 1);std::iota(pos + 1, pos + 1 + n, 1);std::iota(idx + 1, idx + 1 + n, 1);std::iota(g + 1, g + 1 + n, 1);while (q--) {int op = 0;std::cin >> op;if (op == 1) { // 合并到 a 上int a = 0, b = 0;std::cin >> a >> b;merge(pos[a], pos[b]);}else if (op == 2) { // 移动 a 到部落 bint a = 0, b = 0;std::cin >> a >> b;g[a] = pos[b];}else if (op == 3) { // 交换 a b 两个部落int a = 0, b = 0;std::cin >> a >> b;std::swap(idx[pos[a]], idx[pos[b]]);std::swap(pos[a], pos[b]);}else { // 找 a 在那个部落int a = 0;std::cin >> a;std::cout << idx[getfa(g[a])] << '\n';}}
}
1010
(算思维?)
感觉是乱搞搞过去的。
CODE
void solve()
{int n = 0, m = 0;std::cin >> n >> m;std::vector d1(n, 0ll), d2(n, 0ll), d3(n, 0ll), d4(n, 0ll);for (int i = 0; i < n; i++) {i64 x = 0, y = 0;std::cin >> x >> y;d1[i] = x + y;d2[i] = x + Inf - y;d3[i] = Inf - x + Inf - y;d4[i] = Inf - x + y;}std::sort(d1.begin(), d1.end());std::sort(d2.begin(), d2.end());std::sort(d3.begin(), d3.end());std::sort(d4.begin(), d4.end());i64 ans = 2 * Inf;for (int i = 0; i < m; i++) {i64 x = 0, y = 0;std::cin >> x >> y;ans = std::min( ans, std::max({ abs(d1.back() - (x + y)), abs(d2.back() - (x + Inf - y)), abs(d3.back() - (Inf - x + Inf - y)), abs(d4.back() - (Inf - x + y)) }));}std::cout << ans << '\n';
}