Dashboard - Educational Codeforces Round 176 (Rated for Div. 2) - Codeforces
A 贪心 数学 模拟
B 贪心 构造
C 组合数学
D 暴力
Problem - E - Codeforces ^e8e652
题意:
给定 \(n,m,A,B\),你需要计算数组对 \((a,b)\) 的个数,满足 \(a\) 的长度为 \(n\),\(a_i\in [0,A]\),\(b\) 的长度为 \(m\),\(b_i\in [0,B]\),令矩阵 \(X_{i,j}=a_i\oplus b_j\),且 \(X\) 矩阵中至多有两种值。
题解:
如果某一个数组有超过 \(2\) 种值,一定不满足要求,可以分类讨论出以下四种情况:
\(case1\):\(a,b\) 都只有一种值,方案数为 \((A+1)(B+1)\)。
\(case2,3\):\(a,b\) 一个有一种值,一个有两种值,方案数为 $$(2^n-2)\times \binom{A+1}{2}\times(B+1)+(2^m-2)\times \binom{B+1}{2}\times(A+1)$$
\(case4\):\(a,b\) 都各有两种值,设为 \(a_1,a_2,b_1,b_2\),此时需要满足 \(a_1\oplus b_1=a_2\oplus b_2\),化简得到 \(a_1\oplus a_2=b_1\oplus b_2\)。
设这个值为 \(x\),不妨设 \(a_2<a_1,b_2<b_1\),只需要统计以下数对 \((a_1,b_1,x)\) 的个数。
考虑数位 \(dp[i][f_a][f_b][f_x]\) 表示已经确定前 \(i\) 位,\(a_1\) 是否卡住上界,\(b_1\) 是否卡住上界,\(x\) 是否有值的方案数。
当 \(x:0\to 1\) 转移时,需要保证这一位 \(a_1,b_1\) 取值都为 \(1\)。
#include<bits/stdc++.h>
using namespace std;#define ll long long
#define ull unsigned long longll read()
{ll x = 0; bool f = false; char c = getchar();while(c < '0' || c > '9') f |= (c == '-'), c = getchar();while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();return f ? -x : x;
}const ll mod = 998244353;ll qpow(ll a, ll b, ll mod)
{ll ans = 1;while(b){if(b & 1) ans = ans * a % mod;b >>= 1;a = a * a % mod;}return ans;
}ll C2(ll n){ return 1ll * n * (n - 1) / 2 % mod; }ll dp[31][2][2][2];void add(ll &x, ll y){ x = (x + y >= mod) ? x + y - mod : x + y; }void solve()
{ll n = read(), m = read(), A = read(), B = read();ll ans1 = (A + 1) * (B + 1) % mod;ll ans2 = (qpow(2, n, mod) - 2) * C2(A + 1) % mod * (B + 1) % mod;ll ans3 = (qpow(2, m, mod) - 2) * C2(B + 1) % mod * (A + 1) % mod;for(int i = 0; i < 31; ++i)for(int j = 0; j < 2; ++j)for(int k = 0; k < 2; ++k)for(int t = 0; t < 2; ++t)dp[i][j][k][t] = 0;dp[30][1][1][0] = 1;for(int i = 30; i > 0; --i){int flag1 = (A >> (i - 1)) & 1, flag2 = (B >> (i - 1)) & 1;for(int j = 0; j < 2; ++j)for(int k = 0; k < 2; ++k)for(int t = 0; t < 2; ++t)if(dp[i][j][k][t])for(int x = 0; x <= max(flag1, (j ^ 1)); ++x)for(int y = 0; y <= max(flag2, (k ^ 1)); ++y){int to_j = (x == flag1) & j, to_k = (y == flag2) & k;add(dp[i - 1][to_j][to_k][t], dp[i][j][k][t]);if(t == 0 && x == 1 && y == 1) add(dp[i - 1][j][k][1], dp[i][j][k][t]);else if(t == 1) add(dp[i - 1][to_j][to_k][1], dp[i][j][k][t]);}}ll ans4 = (dp[0][0][0][1] + dp[0][0][1][1] + dp[0][1][0][1] + dp[0][1][1][1]) % mod;ll ans5 = (qpow(2, n, mod) - 2) * (qpow(2, m, mod) - 2) % mod;printf("%lld\n", (ans1 + ans2 + ans3 + ans4 * ans5) % mod);
}int main()
{int T = read();while(T--) solve();return 0;
}
Problem - F - Codeforces ^764811
题意:
给定长度为 \(n\) 的整数数组 \(a\),请找出最长的子序列满足 \(a_{l}\) 为严格最小值,\(a_r\) 为严格最大值。
题解:
考虑 \(l\neq l'\),且 \(a_l=a_{l'}\),此时左端点选择 \(a_l\) 一定更优,右端点同理,注意到可能成为答案的左端点一定是前缀严格最小值,右端点一定是后缀严格最大值。
进一步观察到,这些左右端点具有单调性。
如果 \(a_i\) 能为 \((a_l,a_r)\) 产生贡献,一定满足 \(a_l<a_i<a_r\),结合单调性可知一个位置可以贡献的左右端点都是一段区间。
利用扫描线枚举左端点,将每个位置的贡献在左端点差分,线段树维护右端点的答案。
#include<bits/stdc++.h>
using namespace std;#define ll long long
#define ull unsigned long longll read()
{ll x = 0; bool f = false; char c = getchar();while(c < '0' || c > '9') f |= (c == '-'), c = getchar();while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();return f ? -x : x;
}const int inf = 0x3f3f3f3f;
const int N = 5e5 + 5;
int n, a[N];
vector<int> MN, MX;
vector< pair<int, int> > add[N], del[N];
int st1[20][N], st2[20][N], lg[N];int get1(int l, int r)
{int d = lg[r - l + 1];return max(st1[d][l], st1[d][r - (1 << d) + 1]);
}int get2(int l, int r)
{int d = lg[r - l + 1];return min(st2[d][l], st2[d][r - (1 << d) + 1]);
}#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)
int mx[N << 2], lazy[N << 2];void pushup(int k){ mx[k] = max(mx[ls(k)], mx[rs(k)]); }void pushdown(int k)
{mx[ls(k)] += lazy[k], mx[rs(k)] += lazy[k];lazy[ls(k)] += lazy[k], lazy[rs(k)] += lazy[k];lazy[k] = 0;
}void update(int k, int l, int r, int L, int R, int val)
{if(L <= l && r <= R){ mx[k] += val, lazy[k] += val; return ; }pushdown(k);int mid = (l + r) >> 1;if(L <= mid) update(ls(k), l, mid, L, R, val);if(R > mid) update(rs(k), mid + 1, r, L, R, val);pushup(k);
}int query(int k, int l, int r, int L, int R)
{if(L <= l && r <= R) return mx[k];pushdown(k);int mid = (l + r) >> 1;if(R <= mid) return query(ls(k), l, mid, L, R);if(L > mid) return query(rs(k), mid + 1, r, L, R);return max(query(ls(k), l, mid, L, R), query(rs(k), mid + 1, r, L, R));
}void clear(int k, int l, int r)
{mx[k] = lazy[k] = 0;if(l == r) return ;int mid = (l + r) >> 1;clear(ls(k), l, mid), clear(rs(k), mid + 1, r);
}void solve()
{n = read();for(int i = 1; i <= n; ++i) a[i] = read();for(int i = 0; i <= n + 1; ++i) add[i].clear(), del[i].clear();MN.clear(), MX.clear();a[0] = inf, a[n + 1] = 0;MN.emplace_back(0);for(int i = 1; i <= n + 1; ++i){if(a[i] < a[MN.back()]) MN.emplace_back(i);}MX.emplace_back(n + 1);for(int i = n; i >= 0; --i){if(a[i] > a[MX.back()]) MX.emplace_back(i);}reverse(MX.begin(), MX.end());for(int i = 0; i < (int)MN.size(); ++i) st1[0][i] = a[MN[i]];for(int i = 1; (1 << i) <= (int)MN.size(); ++i)for(int j = 0; j + (1 << i) - 1 < (int)MN.size(); ++j)st1[i][j] = max(st1[i - 1][j], st1[i - 1][j + (1 << (i - 1))]);for(int i = 0; i < (int)MX.size(); ++i) st2[0][i] = a[MX[i]];for(int i = 1; (1 << i) <= (int)MX.size(); ++i)for(int j = 0; j + (1 << i) - 1 < (int)MX.size(); ++j)st2[i][j] = min(st2[i - 1][j], st2[i - 1][j + (1 << (i - 1))]);for(int i = 1; i <= n; ++i){int l1 = 1, r1 = lower_bound(MN.begin(), MN.end(), i) - MN.begin() - 1;int l = 0, r = r1;while(l < r){int mid = (l + r + 1) >> 1;if(get1(r1 - mid + 1, r1) >= a[i]) r = mid - 1;else l = mid;}l1 = r1 - l + 1;int l2 = upper_bound(MX.begin(), MX.end(), i) - MX.begin(), r2 = n + 1;l = 0, r = MX.size() - l2 - 1;while(l < r){int mid = (l + r + 1) >> 1;if(get2(l2, l2 + mid - 1) <= a[i]) r = mid - 1;else l = mid;}r2 = l2 + l - 1;if(l2 <= r2 && l1 <= r1){add[l1].emplace_back(pair<int, int>(l2, r2));del[r1 + 1].emplace_back(pair<int, int>(l2, r2));}}int lim1 = (int)MN.size() - 2, lim2 = (int)MX.size() - 2;clear(1, 1, lim2);int ans = 1;for(int i = 1; i <= lim1; ++i){for(auto [l, r] : add[i]) update(1, 1, lim2, l, r, 1);for(auto [l, r] : del[i]) update(1, 1, lim2, l, r, -1);int l3 = upper_bound(MX.begin(), MX.end(), MN[i]) - MX.begin();int l = 0, r = lim2 - l3 + 1;while(l < r){int mid = (l + r + 1) >> 1;if(get2(l3, l3 + mid - 1) <= a[MN[i]]) r = mid - 1;else l = mid;}int r3 = l3 + l - 1;if(l3 <= r3) ans = max(ans, query(1, 1, lim2, l3, r3) + 2);}printf("%d\n", ans);
}int main()
{lg[0] = -1;for(int i = 1; i <= 500000; ++i) lg[i] = lg[i >> 1] + 1;int T = read();while(T--) solve();return 0;
}