ATcoder ABC 357 补题记录(A~F)

A

按照顺序直接模拟即可。

#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define int long long
#define pb push_back
#define em emplace_back
#define F(i,x,y) for(int i=x;i<=y;i++)
#define G(i,x,y) for(int i=x;i>=y;i--)
#define W(G,i,x) for(auto&i:G[x])
#define W_(G,i,j,x) for(auto&[i,j]:G[x])
#define add(x,y) z[x].em(y)
#define add_(x,y) add(x,y),add(y,x)
#define Add(x,y,d) z[x].em(y,d)
#define Add_(x,y,z) Add(x,y,z),Add(y,x,z);
#ifdef int
#define inf (7611257611378358090ll/2)
#else
#define inf 0x3f3f3f3f
#endif
using namespace std;
const int N = 1000100;
int a[N], n;
signed main() {
    int n, m, cnt = 0;
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        int h;
        cin >> h;
        m -= h;
        if (m >= 0) {
            cnt++;
        } else {
            break;
        }
    }
    cout << cnt << '\n';
}

B

直接比较大写字母和小写字母的数量即可。

时间复杂度为 O(n)

#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define int long long
#define pb push_back
#define em emplace_back
#define F(i,x,y) for(int i=x;i<=y;i++)
#define G(i,x,y) for(int i=x;i>=y;i--)
#define W(G,i,x) for(auto&i:G[x])
#define W_(G,i,j,x) for(auto&[i,j]:G[x])
#define add(x,y) z[x].em(y)
#define add_(x,y) add(x,y),add(y,x)
#define Add(x,y,d) z[x].em(y,d)
#define Add_(x,y,z) Add(x,y,z),Add(y,x,z);
#ifdef int
#define inf (7611257611378358090ll/2)
#else
#define inf 0x3f3f3f3f
#endif
using namespace std;
const int N = 1000100;
int a[N];
signed main() {
    string s;
    cin >> s;
    int c1 = 0, c2 = 0;
    for (auto &x : s) {
        if (islower(x)) c1++;
        else c2++;
    }
    if (c1 < c2) {
        for (auto &x : s) x = toupper(x);
        cout << s << '\n';
    } else {
        for (auto &x : s) x = tolower(x);
        cout << s << '\n';
    }
}

C

模拟题。

考虑对于一个 3n×3n 的矩阵,可以将其分割为 93n1×3n1 的矩阵,其中这 9 个矩阵又构成了 3×3 的大矩阵。

因此直接计算出 9 个矩阵的左上角和右下角的坐标,然后除了中间的一个矩阵以外其他的矩阵继续递归处理即可。

时间复杂度为 O(32n)

#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define int long long
#define pb push_back
#define em emplace_back
#define F(i,x,y) for(int i=x;i<=y;i++)
#define G(i,x,y) for(int i=x;i>=y;i--)
#define W(G,i,x) for(auto&i:G[x])
#define W_(G,i,j,x) for(auto&[i,j]:G[x])
#define add(x,y) z[x].em(y)
#define add_(x,y) add(x,y),add(y,x)
#define Add(x,y,d) z[x].em(y,d)
#define Add_(x,y,z) Add(x,y,z),Add(y,x,z);
#ifdef int
#define inf (7611257611378358090ll/2)
#else
#define inf 0x3f3f3f3f
#endif
using namespace std;
const int N = 1000100;
int a[N], n;
char s[5999][5999];
void dfs(int n, int x = 1, int y = 1) {
    if (n == 0) {
        s[x][y] = '#';
    } else {
        int key = 1;
        for (int i = 1; i < n; i++) {
            key = key * 3;
        }
        dfs(n - 1, x, y);
        dfs(n - 1, x + key, y);
        dfs(n - 1, x + key + key, y);
        dfs(n - 1, x, y + key);
        dfs(n - 1, x + key + key, y + key);
        dfs(n - 1, x, y + key + key);
        dfs(n - 1, x + key, y + key + key);
        dfs(n - 1, x + key + key, y + key + key);
    }
}
signed main() {
    cin >> n;
    if (!n) {
        cout << "#\n";
    } else {
        int key = 1;
        for (int i = 0; i < n; i++) {
            key *= 3;
        }
        for (int i = 1; i <= key; i++) {
            for (int j = 1; j <= key; j++) {
                s[i][j] = '.';
            }
        }
        dfs(n);
        for (int i = 1; i <= key; i++, cout << '\n') {
            for (int j = 1; j <= key; j++) {
                cout << s[i][j];
            }
        }
    }
}

D

考虑经典套路。这里重定义 |n| 表示 n 的位数,即 to_string(n).size()(请使用 C++11 及以上版本)。Sn 表示 N=n 时的答案。

首先考虑 |n|1 的情况。此时沿用 P4884 的解决方案。有 Sn=n×10n19

问题是 |n|>1 时的情况。容易发现此时等式 Sn=n×10n×|n|10k1 成立。证明显然。

然后考虑计算这个东西。当 n=1018n×|n|=1018×19=1.9×1018>2641 是很恶心的一点,所以请使用 __int128 来计算答案。其实也可以使用 atcoder::modint998244353 来计算答案。

#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define int long long
#define pb push_back
#define em emplace_back
#define F(i,x,y) for(int i=x;i<=y;i++)
#define G(i,x,y) for(int i=x;i>=y;i--)
#define W(G,i,x) for(auto&i:G[x])
#define W_(G,i,j,x) for(auto&[i,j]:G[x])
#define add(x,y) z[x].em(y)
#define add_(x,y) add(x,y),add(y,x)
#define Add(x,y,d) z[x].em(y,d)
#define Add_(x,y,z) Add(x,y,z),Add(y,x,z);
#ifdef int
#define inf (7611257611378358090ll/2)
#else
#define inf 0x3f3f3f3f
#endif
using namespace std;
const int mod = 998244353;
int ksm(int a, __int128 b, int c) {
    if (!b) return 1;
    int ans = ksm(a, b / 2, c);
    ans = ans * ans % c;
    if (b & 1) ans = ans * a % c;
    return ans;
}
signed main() {
    int n;
    cin >> n;
    int len = to_string(n).size();
    int key = ksm(10, (__int128)(len) * n, mod);
    key = (key + mod - 1) % mod;
    int ii = ksm((ksm(10, len, mod) + mod - 1) % mod, mod - 2, mod);
    // cout << "dbg " << ii << ' ' << key << '\n';
    cout << (n % mod) * ii % mod * key % mod << '\n';
}

E

不理解为什么题目保证边数不大于点数。这个题 m>n 不是也可以做吗。

首先建反图,然后考虑使用 Tarjan 算法将有向图中的每一个强连通分量在新图中缩为一个点,然后剩下一个 DAG 森林考虑搞一下。

fi 表示可以到达新图 i 点的点的数量,gi 表示以新图 i 点结尾的不同的路径的总数目。

那么对于每一条 ji 的边,必然有:

  • fi=fi+fj
  • gi=gi+fj×CNTi

其中 CNTi 表示新图中 i 点在原图中对应的强连通分量中点的数目。

因为新的图是 DAG 森林,所以每一次从入度为 0 的结点开始转移,在拓扑排序的过程中就可以直接求出答案。

因为不同的 i 对应的 gi 对应的路径是互不重复的,所以答案就是 i=1ngi,不需要容斥。

时间复杂度为 O(n+m)。如果写丑了可能会出问题。

#include<bits/stdc++.h>
#define int long long
#define said(...)
#define pb push_back
#define em emplace_back
#define F(i,x,y) for(int i=x;i<=y;++i)
#define G(i,x,y) for(int i=x;i>=y;--i)
#define W(G,i,x) for(auto&i:G[x])
#define W_(G,i,j,x) for(auto&[i,j]:G[x])
#define add(x,y) z[x].em(y)
#define add_(x,y) add(x,y),add(y,x)
#define Add(x,y,d) z[x].em(y,d)
#define Add_(x,y,z) Add(x,y,z),Add(y,x,z);
#define inf (7611257611378358090ll/2)
using namespace std;
const int N = 500100;
const int mod = 998244353;

stack<int> stk;
vector<int> z[N], scc[N];
int idx, tot, instk[N], dfn[N], low[N], bel[N], cnt[N], dis[N], vis[N], en[N], deg[N];
struct Edg { int u, v; } ed[N];
void dfs(int u) {
    dfn[u] = low[u] = ++idx;
    stk.push(u), instk[u] = true;
    W(z, j, u) {
        if (!dfn[j]) {
            dfs(j);
            low[u] = min(low[u], low[j]);
        } else if (instk[j]) {
            low[u] = min(low[u], dfn[j]);
        }
    }
    if (dfn[u] == low[u]) {
        tot++;
        while (stk.top() != u) {
            int t = stk.top();
            stk.pop(), instk[t] = false;
            bel[t] = tot, scc[tot].pb(t);
        }
        int t = stk.top();
        stk.pop(), instk[t] = false;
        bel[t] = tot, scc[tot].pb(t);
    }
}
int a[N], dp[N], gg[N];
auto main() [[O3]] -> signed {
    int n;
    cin >> n;
    F(i, 1, n) {
        int x;
        cin >> x;
        a[i] = x;
        z[i].push_back(x);
    }
    F(i, 1, n) {
        if (!dfn[i]) {
            dfs(i);
        }
    }
    F(i, 1, n) {
        z[i].clear();
    }
    F(i, 1, n) {
        if (bel[i] != bel[a[i]]) {
            deg[bel[i]]++;
            z[bel[a[i]]].push_back(bel[i]);
        }
    }
    queue<int> q;
    F(i, 1, tot) {
        if (!deg[i]) {
            q.push(i);
        }
        gg[i] = scc[i].size();
        dp[i] = scc[i].size() * scc[i].size();
    }
    while (q.size()) {
        int f = q.front();
        q.pop();
        for (auto &g : z[f]) {
            gg[g] = (gg[g] + gg[f]) % mod;
            dp[g] = (dp[g] + scc[g].size() * gg[f] % mod) % mod;
            if (!--deg[g]) {
                q.push(g);
            }
        }
    }
    cout << accumulate(dp + 1, dp + tot + 1, 0ll) << '\n';
}

F

区间修改区间查询很显然的线段树。

考虑对于线段树的每一个结点维护 A 的和,B 的和,A 的懒标记,B 的懒标记,当前区间内的答案的和。

首先 push_up 的时候直接将 AB 的和和答案的和全部直接把左右儿子的值加起来即可。

修改的时候:

  • 若当前修改的是 A 的值,则答案从 al×bl+al+1×bl+1++ar×br 变为了 (al+v)×bl+(al+1+v)×bl+1++(a+r+v)×br。显然后面的式子为 (al×bl+al+1×bl+1++ar×br)+v×(bl+bl+1+bl+2++br)=(al×bl+al+1×bl+1++ar×br)+v×B。所以更新答案的时候直接把 A 加上 (rl+1)×v,总和加上 v×B 即可。
  • 若当前修改的为 B 的值,则和 A 的情况一样,B 加上 (rl+1)×v,总和加上 v×A 即可。

下传标记直接按照 AB 的顺序下传即可,时间复杂度为 O(nlogn)。注意不要爆 long long

#include<bits/stdc++.h>
#define int long long
#define said(...)
#define pb push_back
#define em emplace_back
#define F(i,x,y) for(int i=x;i<=y;++i)
#define G(i,x,y) for(int i=x;i>=y;--i)
#define W(G,i,x) for(auto&i:G[x])
#define W_(G,i,j,x) for(auto&[i,j]:G[x])
#define add(x,y) z[x].em(y)
#define add_(x,y) add(x,y),add(y,x)
#define Add(x,y,d) z[x].em(y,d)
#define Add_(x,y,z) Add(x,y,z),Add(y,x,z);
#define inf (7611257611378358090ll/2)
using namespace std;
const int N = 200100;
const int mod = 998244353;

int a[N], b[N];
struct Node {
    int l, r, a, b, taga, tagb, sum;
    void init(int p) {
        l = r = p;
        a = ::a[p];
        a %= mod;
        b = ::b[p];
        b %= mod;
        taga = tagb = 0;
        sum = a * b % mod;
    }
    void c1(int v) {
        v %= mod;
        taga += v, a += (r - l + 1) * v;
        sum += v * b; sum %= mod;
        taga %= mod, a %= mod;
        // sum += b * v;
    }
    void c2(int v) {
        v %= mod;
        tagb += v, b += (r - l + 1) * v;
        sum += v * a % mod; sum %= mod;
        tagb %= mod, b %= mod;
        // sum = a * b;
    }
} z[N << 2];
Node operator+(const Node &l, const Node &r) {
    Node res;
    res.l = l.l, res.r = r.r;
    res.a = l.a + r.a, res.b = l.b + r.b, res.sum = l.sum + r.sum;
    res.a %= mod, res.b %= mod, res.sum %= mod;
    res.taga = res.tagb = 0;
    return res;
}
void build(int l, int r, int rt) {
    if (l == r) {
        return z[rt].init(l);
    }
    int mid = l + r >> 1;
    build(l, mid, rt << 1);
    build(mid + 1, r, rt << 1 | 1);
    z[rt] = z[rt << 1] + z[rt << 1 | 1];
}
void pud(int rt) {
    if (z[rt].taga != 0) {
        z[rt << 1].c1(z[rt].taga);
        z[rt << 1 | 1].c1(z[rt].taga);
        z[rt].taga = 0;
    }
    if (z[rt].tagb != 0) {
        z[rt << 1].c2(z[rt].tagb);
        z[rt << 1 | 1].c2(z[rt].tagb);
        z[rt].tagb = 0;
    }
}
void modify1(int l, int r, int rt, int ll, int rr, int v) {
    if (ll <= l && r <= rr) {
        return z[rt].c1(v);
    }
    int mid = l + r >> 1;
    pud(rt);
    if (ll <= mid) {
        modify1(l, mid, rt << 1, ll, rr, v);
    }
    if (mid < rr) {
        modify1(mid + 1, r, rt << 1 | 1, ll, rr, v);
    }
    z[rt] = z[rt << 1] + z[rt << 1 | 1];
}
void modify2(int l, int r, int rt, int ll, int rr, int v) {
    if (ll <= l && r <= rr) {
        return z[rt].c2(v);
    }
    int mid = l + r >> 1;
    pud(rt);
    if (ll <= mid) {
        modify2(l, mid, rt << 1, ll, rr, v);
    }
    if (mid < rr) {
        modify2(mid + 1, r, rt << 1 | 1, ll, rr, v);
    }
    z[rt] = z[rt << 1] + z[rt << 1 | 1];
}
int query(int l, int r, int rt, int ll, int rr, int v) {
    if (ll <= l && r <= rr) {
        return z[rt].sum;
    }
    int mid = l + r >> 1;
    pud(rt);
    int s = 0;
    if (ll <= mid) {
        s = (s + query(l, mid, rt << 1, ll, rr, v)) % mod;
    }
    if (mid < rr) {
        s = (s + query(mid + 1, r, rt << 1 | 1, ll, rr, v)) % mod;
    }
    return s % mod;
}
auto main() [[O3]] -> signed {
    int n, q;
    cin >> n >> q;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= n; i++) cin >> b[i];
    build(1, n, 1);
    while (q--) {
        int o, l, r, x;
        cin >> o >> l >> r;
        if (o < 3) {
            cin >> x;
        }
        if (o == 1) {
            modify1(1, n, 1, l, r, x);
        } else if (o == 2) {
            modify2(1, n, 1, l, r, x);
        } else {
            cout << query(1, n, 1, l, r, x) << '\n';
        }
    }
}

G

咕咕咕。

posted @   yhbqwq  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
点击右上角即可分享
微信分享提示