Educational Codeforces Round 176

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 long

ll 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 long

ll 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;
}
posted @ 2025-03-22 13:23  梨愁浅浅  阅读(47)  评论(1)    收藏  举报