Codeforces Round 1083 (Div. 2) 解题报告

Dashboard - Codeforces Round 1083 (Div. 2) - Codeforces

Problem - A - Codeforces

将第一个值与最大值交换,此时只有之后一个丑位置。

点击展开代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long
#define i128 __int128
#define vi vector<int>
#define pii pair<int, int>
#define pll pair<ll, ll>
#define siz(a) ((int)((a).size()))
#define endl '\n'
// #define fi first
// #define se second
// #define double long double
// #define int ll

void solve()
{
    int n;
    cin >> n;
    vector<int> a(n);
    for(int i = 0; i < n; ++i) cin >> a[i];
    int pos = 0;
    for(int i = 1; i < n; ++i) if(a[i] == n) pos = i;
    swap(a[0], a[pos]);
    for(auto x : a) cout << x << ' ';
    cout << endl;
}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int T = 1;
    cin >> T;
    while(T--) solve();
    return 0;
}

Problem - B - Codeforces

由于 \(p^p\) 总可以被 \(p\) 整除。因此只要保证 \(k\) 包含 \(n\) 的所有质因子即可,即 \(k\)\(n\) 的所有质因子相乘。

点击展开代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long
#define i128 __int128
#define vi vector<int>
#define pii pair<int, int>
#define pll pair<ll, ll>
#define siz(a) ((int)((a).size()))
#define endl '\n'
// #define fi first
// #define se second
// #define double long double
// #define int ll

void solve()
{
    int n;
    cin >> n;
    ll ans = 1;
    for(int i = 2; i * i <= n; ++i)
    {
        if(n % i == 0)
        {
            ans *= i;
            while(n % i == 0) n /= i;
        }
    }
    if(n > 1) ans *= n;
    cout << ans << endl;
}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int T = 1;
    cin >> T;
    while(T--) solve();
    return 0;
}

倒序考虑操作,相当于每次选一个序列放在已有序列的末尾,要求字典序最小。
由于一个元素只有第一次出现的位置有用,因此直接将剩余序列排序后,选最小的序列拼接上即可。再模拟删除剩余未选的序列中,已经被选上的元素。

点击展开代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long
#define i128 __int128
#define vi vector<int>
#define pii pair<int, int>
#define pll pair<ll, ll>
#define siz(a) ((int)((a).size()))
#define endl '\n'
// #define fi first
// #define se second
// #define double long double
// #define int ll

void solve()
{
    int n;
    cin >> n;
    vector<vector<int>> a(n + 1);
    vector<int> b;
    for(int i = 1; i <= n; ++i)
    {
        int siz, x;
        cin >> siz;
        a[i].resize(siz);
        for(int j = 0; j < siz; ++j)
        {
            cin >> x;
            a[i][j] = x;
            b.emplace_back(x);
        }
    }
    sort(b.begin(), b.end());
    b.resize(unique(b.begin(), b.end()) - b.begin());
    for(int i = 1; i <= n; ++i)
    {
        for(auto &x : a[i]) x = lower_bound(b.begin(), b.end(), x) - b.begin();
        reverse(a[i].begin(), a[i].end());
    }
    vector<int> v1(n + 1, 0), v2(siz(b) + 1, 0);
    vector<int> ans;

    for(int i = 1; i <= n; ++i)
    {
        vi temp;
        for(auto x : a[i])
        {
            if(!v2[x]) temp.emplace_back(x), v2[x] = 1;
        }
        a[i] = temp;
        for(auto x : a[i]) v2[x] = 0;
    }

    for(int i = 1; i <= n; ++i)
    {
        vector<int> id;
        for(int j = 1; j <= n; ++j)
        {
            if(v1[j]) continue;
            id.emplace_back(j);
        }
        sort(id.begin(), id.end(), [&](int x, int y){ return a[x] < a[y]; });
        for(auto x : a[id[0]]) if(!v2[x]) ans.emplace_back(x), v2[x] = 1;
        v1[id[0]] = 1;
        // printf("id[0] = %d\n", id[0]);
        // for(auto x : a[id[0]]) printf("%d ", x);
        // printf("\n");
        for(int j = 1; j < siz(id); ++j)
        {
            vector<int> temp;
            for(auto x : a[id[j]]) if(!v2[x]) temp.emplace_back(x);
            a[id[j]] = temp;
        }
    } 
    // reverse(ans.begin(), ans.end());
    for(auto x : ans) cout << b[x] << ' ';
    cout << endl;
}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int T = 1;
    cin >> T;
    while(T--) solve();
    return 0;
}

Problem - D - Codeforces

注意到一开始会选择最大值,然后清空它的左侧或右侧,对于另一侧,最大值已经没有限制,于是变成一个相同的子问题。

于是求出序列的笛卡尔树,问题转化成在笛卡尔树上DP,设 \(dp_i\) 表示处理完 \(i\) 子树的最小操作数,转移有

\[dp_x=\min(dp_{ls(x)}+size(rs(x)), dp_{rs(x)}+size(ls(x))) \]

点击展开代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long
#define i128 __int128
#define vi vector<int>
#define pii pair<int, int>
#define pll pair<ll, ll>
#define siz(a) ((int)((a).size()))
#define endl '\n'
// #define fi first
// #define se second
// #define double long double
// #define int ll

const int N = 5e5 + 5;
int n, a[N];
int son[N][2];
int sta[N], top;

#define ls(x) son[x][0]
#define rs(x) son[x][1]

int dp[N], Siz[N];
void dfs(int k)
{
    Siz[k] = 1;
    if(ls(k)) dfs(ls(k)), Siz[k] += Siz[ls(k)];
    if(rs(k)) dfs(rs(k)), Siz[k] += Siz[rs(k)];
    if(!ls(k) || !rs(k)){ dp[k] = dp[ls(k)] + dp[rs(k)]; return ; }
    dp[k] = min(Siz[ls(k)] + dp[rs(k)], Siz[rs(k)] + dp[ls(k)]);
}

void solve()
{
    cin >> n;
    for(int i = 1; i <= n; ++i) cin >> a[i], ls(i) = rs(i) = 0;
    top = 0;
    for(int i = 1; i <= n; ++i)
    {
        while(top && a[sta[top]] < a[i]) ls(i) = sta[top], --top;
        rs(sta[top]) = i;
        sta[++top] = i;
    }
    while(top) rs(sta[top - 1]) = sta[top], --top;
    dfs(rs(0));
    cout << dp[rs(0)] << endl;
}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int T = 1;
    cin >> T;
    while(T--) solve();
    return 0;
}

Problem - E - Codeforces

问题在于同一个 \(S\) 可能有很多种翻转方式到达 \(T\)
注意到一个子串若含有border,设为 \(s=twt\),那么翻转 \(s\) 等价于分别翻转 \(t,w,t\)。而这是最小分割的方式,以此来做DP

\(dp_{i}\) 表示考虑前 \(i\) 个字符时,有多少字符串 \(S\) 满足可以翻转得到 \(T\),转移时枚举上一个断点 \(j\),满足 \(T[i...j]\) 没有border

可以对于 \(T\) 的每一个后缀做一遍KMP,用bool数组存储下是否有border

点击展开代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long
#define i128 __int128
#define vi vector<int>
#define pii pair<int, int>
#define pll pair<ll, ll>
#define siz(a) ((int)((a).size()))
#define endl '\n'
// #define fi first
// #define se second
// #define double long double
// #define int ll

const int N = 8005;
int n, a[N], b[N];
int nxt[N];
bool f[N][N];
const ll mod = 998244353;
int dp[N];

void add(int &x, int y){ x = (x + y >= mod) ? (x + y - mod) : (x + y); }
void del(int &x, int y){ x = (x - y < 0) ? (x - y + mod) : (x - y); }

void solve()
{
    cin >> n;
    for(int i = 1; i <= n; ++i) cin >> a[i];
    for(int i = 1; i <= n; ++i)
    {
        for(int j = i; j <= n; ++j) f[i][j] = 0;
        int len = n - i + 1;
        for(int j = 1; j <= len; ++j) b[j] = a[i + j - 1];
        nxt[1] = 0;
        for(int j = 2, k = 0; j <= len; ++j)
        {
            while(k && b[k + 1] != b[j]) k = nxt[k];
            if(b[k + 1] == b[j]) ++k;
            nxt[j] = k;
        }
        for(int j = 1; j <= len; ++j) f[i][j + i - 1] = (nxt[j] == 0);
    }
    dp[0] = 1;
    for(int i = 1; i <= n; ++i)
    {
        dp[i] = 0;
        for(int j = 1; j <= i; ++j)
            if(f[j][i]) add(dp[i], dp[j - 1]);
    }
    cout << dp[n] << endl;
}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int T = 1;
    cin >> T;
    while(T--) solve();
    return 0;
}

Problem - F - Codeforces

先不考虑不可重建的边。由于每个点度数都为偶数,那么最终有一条欧拉回路。

考虑只有一条简单欧拉回路,贡献是左边+上边-右边-下边,如果每个格子的价值是它的左边+上边-右边-下边,那么整个欧拉回路的价值恰好是被包围在内的格子的价值之和。

再考虑有多条欧拉回路,发现一个格子被奇数个欧拉回路包含时才有贡献,再进一步发现,每个格子是否选择时相互独立的。

此时考虑不可重建的边,若该边位于边界,那么边界那个格子不能选择;若位于两个格子中间,那么这两个格子必须一起选或者一起不选,使用并查集维护一下。

点击展开代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long
#define i128 __int128
#define vi vector<int>
#define pii pair<int, int>
#define pll pair<ll, ll>
#define siz(a) ((int)((a).size()))
#define endl '\n'
// #define fi first
// #define se second
// #define double long double
// #define int ll

const int N = 2e5 + 5;
int n, m;
vector<int> id[N];
vector<int> h[N], l[N];
int f[N * 2];
ll val[N * 2];
const ll inf = 0x3f3f3f3f3f3f3f3f;

int find(int x){ return (x == f[x]) ? x : (f[x] = find(f[x])); }

void merge(int x, int y)
{
    x = find(x), y = find(y);
    if(x == y) return ;
    f[y] = x, val[x] = max(val[x] + val[y], -inf);
}

void solve()
{
    cin >> n >> m;
    for(int i = 1; i <= n; ++i)
    {
        id[i].clear();
        h[i].clear();
        id[i].resize(m + 1);
        h[i].resize(m + 1);
    }
    for(int i = 1; i <= n; ++i)
    {
        l[i].clear();
        l[i].resize(m + 1);
    }
    int tot = 0;
    for(int i = 1; i < n; ++i)
        for(int j = 1; j < m; ++j)
        {
            id[i][j] = ++tot;
            f[tot] = tot;
            val[tot] = 0;
        }
    for(int i = 1; i < n; ++i)
    {
        for(int j = 1; j <= m; ++j)
        {
            cin >> h[i][j];
        }
    }
    for(int i = 1; i <= n; ++i)
        for(int j = 1; j < m; ++j)
            cin >> l[i][j];
    for(int i = 1; i < n; ++i)
        for(int j = 1; j < m; ++j)
        {
            val[id[i][j]] = 1ll * h[i][j] - h[i][j + 1] + l[i][j] - l[i + 1][j];
            // printf("i = %d, j = %d, %d %d %d %d %lld\n", i, j, h[i][j], h[i][j + 1], l[i][j], l[i + 1][j], val[id[i][j]]);
        }
    for(int i = 1; i < n; ++i)
    {
        string s;
        cin >> s;
        for(int j = 1; j <= m; ++j)
        {
            int p;
            p = s[j - 1] - '0';
            if(!p)
            {
                if(j == 1 || j == m) val[find(id[i][j - (j == m)])] = -inf;
                else merge(id[i][j], id[i][j - 1]);
            }
        }
    }
    for(int i = 1; i <= n; ++i)
    {
        string s;
        cin >> s;
        for(int j = 1; j < m; ++j)
        {
            int p;
            p = s[j - 1] - '0';
            if(!p)
            {
                if(i == 1 || i == n) val[find(id[i - (i == n)][j])] = -inf;
                else merge(id[i][j], id[i - 1][j]);
            }
        }
    }
    ll ans = 0;
    for(int i = 1; i < n; ++i)
        for(int j = 1; j < m; ++j)
            if(find(id[i][j]) == id[i][j] && val[id[i][j]] > 0)
            {
                // printf("id[%d][%d] = %d, val = %lld\n", i, j, id[i][j], val[id[i][j]]);
                ans += val[id[i][j]];
            }
    cout << ans << endl;
}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int T = 1;
    cin >> T;
    while(T--) solve();
    return 0;
}

Problem - G - Codeforces

首先 \(x,y\) 没什么用,转化为 \(\gcd(i\oplus j,j\oplus k)|n\)

先不考虑 \(i=j\) 或者 \(j=k\),设 \(p=i\oplus j,q=j\oplus k\)。则 \(p,q\) 都是正整数,设 \(c(p,q)\) 表示 \(i\oplus j=p,j\oplus k=q\) 的方案数。

\[\begin{aligned}&\sum_{i,j,k}[\gcd(i\oplus j,j\oplus k)|n]\\ =& \sum_{p,q}[\gcd(p,q)|n]c(p,q)\\ =& \sum_{d|n}\sum_{p,q}[\gcd(\frac{p}{d},\frac{q}{d})=1]c(p,q)\\ =& \sum_{d|n}\sum_{p,q}c(p,q)\sum_{k|\gcd(\frac{p}{d},\frac{q}{d})}u(k)\\ =& \sum_{T}\sum_{d|\gcd(n,T)}u(\frac{T}{d})\sum_{p,q}c(Tp,Tq) \end{aligned}\]

\(f(T)=\sum_{p,q}c(Tp,Tq),g(T)=\sum_{d|\gcd(n,T)}u(\frac{T}{d})\)

对于 \(g(T)\) 是好求的。考虑求 \(f(T)\)
要求 \(i\oplus j=Tp,j\oplus k=Tq\),这里 \(Tp\le 2m\),而 \(p,q\) 任意,枚举 \(p\),求有多少个 \(j\) 满足 \(0\le Tp\oplus j\le m\)。满足条件的 \(j\)\(\log m\) 个区间上,进行区间+1;而 \(p,q\) 等价,则 \(f(T)=\sum ((cnt_j)^2)\)

可以先枚举 \(p\) 进行区间+1求出所有的 \(cnt_j\),再枚举 \(q\) (实际上一样的过程),此时统计答案,由于操作时区间+1,那么对答案的贡献就是 \(\sum cnt\times 1=\sum cnt\),即区间求和。

但是如果使用树状数组或者线段树,复杂度是 \(O(m\log^3 m)\),不可接收。
注意到满足条件的区间在 \(trie\) 树上恰好是一颗子树,可以通过记录子树和以及标记永久化,在递归时求得子树的 \(\sum cnt\),复杂度为 \(O(m\log^2 m)\)

而对于 \(i=j\) 或者 \(j=k\),实际上是 \(f(T)=\sum cnt_j\)

点击展开代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long
#define i128 __int128
#define vi vector<int>
#define pii pair<int, int>
#define pll pair<ll, ll>
#define siz(a) ((int)((a).size()))
#define endl '\n'
// #define fi first
// #define se second
// #define double long double
// #define int ll

mt19937 rng((unsigned)chrono::steady_clock::now().time_since_epoch().count());
int rnd(int l, int r) {
    return uniform_int_distribution<int>(l, r)(rng);
}

const int N = 6e5 + 5;
int n, m;
int u[N], prime[N / 8], cnt;
bool vis[N];
vector<int> D[N];
ll g[N], f[N];

int ch[1 << 21][2], Size[1 << 21];
ll sum[1 << 21], tag[1 << 21];
int tot, root;

#define ls(x) ch[x][0]
#define rs(x) ch[x][1]

void dfs(int k, int dep, int val)
{
    if(dep == 0)
    {
        Size[k] = 1;
        return ;
    }
    ls(k) = ++tot;
    dfs(ls(k), dep - 1, val);
    if(val + (1 << (dep - 1)) <= m)
    {
        rs(k) = ++tot;
        dfs(rs(k), dep - 1, val + (1 << (dep - 1)));
    }
    Size[k] = Size[ls(k)] + Size[rs(k)];
}

void Init()
{
    u[1] = 1;
    for(int i = 2; i <= N - 5; ++i)
    {
        if(!vis[i]) prime[++cnt] = i, u[i] = -1;
        for(int j = 1; j <= cnt && i * prime[j] <= N - 5; ++j)
        {
            vis[i * prime[j]] = 1;
            if(i % prime[j] == 0){ u[i * prime[j]] = 0; break; }
            else u[i * prime[j]] = -u[i];
        }
    }
    for(int i = 1; i <= N - 5; ++i)
        for(int j = i; j <= N - 5; j += i)
            D[j].emplace_back(i);
}

void getg(int lim)
{
    for(int i = 1; i <= m + m; ++i) g[i] = 0;
    for(int i = 1; i <= m + m; ++i)
    {
        int x = gcd(i, n);
        for(auto d : D[x]) g[i] += u[i / d];
    }
}

int fa[23], num;
void dfs1(int k, int dep, int temp)
{
    num = 0;
    fa[++num] = k;
    for(int i = dep - 1; i >= 0; --i)
    {
        int d = (temp >> i) & 1;
        int t = (m >> i) & 1;
        if(t)
        {
            if(ch[k][d])
            {
                ++tag[ch[k][d]];
                sum[ch[k][d]] += Size[ch[k][d]];
            }
            k = ch[k][d ^ 1];
        }else k = ch[k][d];
        if(!k) break;
        fa[++num] = k;
    }
    if(k)
    {
        sum[k] += Size[k];
        ++tag[k];
    }
    for(int i = num; i >= 1; --i)
        sum[fa[i]] = sum[ls(fa[i])] + sum[rs(fa[i])] + tag[fa[i]] * Size[fa[i]];
}

void dfs3(int k, int dep, int temp)
{
    sum[k] = tag[k] = 0;
    for(int i = dep - 1; i >= 0; --i)
    {
        int d = (temp >> i) & 1;
        int t = (m >> i) & 1;
        if(t)
        {
            tag[ch[k][d]] = 0;
            sum[ch[k][d]] = 0;
            k = ch[k][d ^ 1];
        }else k = ch[k][d];
        sum[k] = tag[k] = 0;
    }
}

ll ans;

void dfs2(int k, int dep, int temp, int stag)
{
    stag += tag[k];
    for(int i = dep - 1; i >= 0; --i)
    {
        int d = (temp >> i) & 1;
        int t = (m >> i) & 1;
        if(t)
        {
            ans += sum[ch[k][d]] + 1ll * stag * Size[ch[k][d]];
            k = ch[k][d ^ 1];
        }else k = ch[k][d];
        stag += tag[k];
    }
    stag -= tag[k];
    ans += sum[k] + 1ll * stag * Size[k];
}

void getf(int lim)
{
    for(int T = 1; T <= m + m; ++T)
    {
        for(int k = 1; k * T <= m + m; ++k)
        {
            dfs1(root, lim, k * T);
        }
        ans = sum[root] * 2;
        for(int k = 1; k * T <= m + m; ++k)
        {
            dfs2(root, lim, k * T, 0);
        }
        f[T] = ans;
        for(int k = 1; k * T <= m + m; ++k)
        {
            dfs3(root, lim, k * T);
        }
    }
}

void solve()
{
    cin >> n >> m;
    int lim = 0;
    while((1 << lim) <= m + m) ++lim;
    root = ++tot;
    // cerr << "lim = " << lim << endl;
    dfs(root, lim, 0);
    getg(lim);
    getf(lim);
    for(int i = 1; i <= tot; ++i)
    {
        sum[i] = tag[i] = Size[i] = 0;
        ls(i) = rs(i) = 0;
    }
    tot = 0;
    i128 ans = 0;
    for(int T = 1; T <= m + m; ++T) ans += (i128)f[T] * g[T];
    cout << (ll)ans << endl;
}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    Init();
    int T = 1;
    cin >> T;
    while(T--) solve();
    return 0;
}
posted @ 2026-04-23 16:49  梨愁浅浅  阅读(15)  评论(0)    收藏  举报