2025.8.21 Codeforces Round 1043 (Div. 3)

[比赛链接](https://codeforces.com/contest/2132)

Solved: 6/7

什么 div3 塞个数位 dp 到 D 题啊???


A. Homework

题意

给三个长度分别为 \(n, m, m\) 的字符串 \(a, b, o\)。依次对 \(a\)\(m\) 次操作:

  • \(o_i = V\),则将 \(b_i\) 插入 \(a\) 的开头;
  • \(o_i = D\),则将 \(b_i\) 插入 \(a\) 的结尾。

输出操作结束后的 \(a\) 串。

题解

void solve(){
    int n, m;
    string a, b, op;
    cin >> n >> a >> m >> b >> op;
    for (int i=0; i<m; ++i)
    {
        if (op[i] == 'V') a = b[i] + a;
        else a = a + b[i];
    }
    cout << a << '\n';
}

B. The Secret Number

题意

已知 \(n = x + y, y = 10^kx\),求 \(x\) 所有可能的值。\(n\leq 10^{18}\)

题解

枚举 \(k\)

void solve(){
    ll n; cin >> n;
    ll x = 10;
    vector <ll> ans;
    for (int i=0; i<18 && x<n; ++i)
    {
        if (!(n % (x+1))) ans.push_back(n / (x+1));
        x *= 10;
    }
    reverse(all(ans));
    cout << ans.size() << '\n';
    for (ll x: ans) cout << x << ' ';
    if (ans.size()) cout << '\n';
}

C1. The Cunning Seller (easy version)

题意

某商品定价每 \(3^x\)\(3^{x+1} + 3^{x-1}x\) 元,买恰好 \(n\) 件该商品,要求交易次数最少,求总花费。\(n\leq 10^{18}\)

题解

\(n\) 转三进制后每一位分别计算花费。

void solve(){
    ll n; cin >> n;
    ll x = 0, p = 1, ans = 0;
    while (n)
    {
        ans += (p*3 + x * (p/3)) * (n%3);
        n /= 3;
        ++x, p *= 3;
    }
    cout << ans << '\n';
}

C2. The Cunning Seller (hard version)

题意

某商品定价每 \(3^x\)\(3^{x+1} + 3^{x-1}x\) 元,买恰好 \(n\) 件该商品,要求交易次数不超过 \(k\) 的前提下总花费最少,求总花费。\(n\leq 10^{18}\)

题解

\(x\) 越大,单位商品的花费越多,因此在 C1 的基础上从大到小把一次 \(3^x\) 的交易换成三次 \(3^{x-1}\) 的交易。每换一个可以减少 \(3^{x-1}\) 的花费。

ll p[20];
void init(int n)
{
    p[0] = 1;
    for (int i=1; i<20; ++i) p[i] = p[i-1] * 3;
}

void solve(){
    ll n, k; cin >> n >> k;
    int x = 0;
    ll ans = 0;
    vector <ll> d;
    while (n)
    {
        int r = n%3;
        d.push_back(r); k -= r;
        ans += (p[x+1] + x * p[x-1]) * r;
        n /= 3; ++x;
    }
    if (k<0)
    {
        cout << "-1\n";
        return;
    }
    for (int i=x-1; i>0; --i)
    {
        if (k > d[i] * 2)
        {
            k -= d[i] * 2;
            ans -= d[i] * p[i-1];
            d[i-1] += d[i] * 3;
            d[i] = 0;
        }
        else
        {
            ll r = k/2;
            ans -= r * p[i-1];
            break;
        }
    }
    cout << ans << '\n';
}

int main(){
    init(20);
    ios::sync_with_stdio(false);cin.tie(0);
    int T; cin>>T;
    while(T--)solve();
}

D. From 1 to Infinity

题意

把自然数从 \(1\) 开始依次写成一个数串:1234567891011121314...,求它的前 \(k\) 位数字和。

题解

给一个不用数位dp的写法。首先算出写到哪个数,假设为 \(v\)。然后统计 1 到 9 每个数在每个数位的出现次数。
数字 \(x\) 在第 \(k\) 位的出现次数是

\[10^k (\lfloor \frac{v}{10^{k+1}} \rfloor + [v_k > x] + [v_k = x](v \bmod 10^k + 1) \]

其中 \(v_k\)\(V\) 的第 \(k\) 位数字。

ll p[16];
void init(int n)
{
    p[0] = 1;
    for (int i=1; i<n; ++i) p[i] = p[i-1] * 10;
}

void solve(){
    ll n; cin >> n;
    ll lim = 0, ans = 0;
    for (int d=0; d<15; ++d)
    {
        if (n > (d+1) * 9ll * p[d])
            n -= (d+1) * 9ll * p[d];
        else
        {
            lim = (d ? p[d]-1 : 0) + n / (d+1);
            n %= d+1;
            ll z = lim+1;
            vector <int> t;
            while (z)
            {
                t.push_back(z % 10);
                z /= 10;
            }
            for (int i=0; i<n; ++i)
                ans += t[t.size() - i - 1];
            break;
        }
    }
    for (int num=1; num<=9; ++num)
    {
        ll tim = 0;
        for (int k=0; k<15; ++k)
        {
            tim += (lim / p[k+1]) * p[k];
            int o = (lim / p[k]) % 10;
            if (o > num) tim += p[k];
            else if (o == num) tim += lim % p[k] + 1;
        }
        ans += tim * num;
    }
    cout << ans << '\n';
}

int main(){
    init(16);
    ios::sync_with_stdio(false);cin.tie(0);
    int T=1; cin>>T;
    //while(T--)cout<<(solve()?"YES":"NO")<<'\n';
    while(T--)solve();
}

E. Arithmetics Competition

题意

给两个数组 \(a\)\(b\),多次询问给定 \(x,y,z\),在 \(a\) 中选择不超过 \(x\) 个数,在 \(b\) 中选择不超过 \(y\) 个数,一共选择恰好 \(z\) 个数,求选出的数的和的最大值。

题解

先将两个数组从大到小排序,预处理 \(a\) 中大于 \(b_i\) 的最小值位置 \(p_i\)\(b\) 中大于 \(a_i\) 的最小值位置 \(q_i\)

假设最优决策下 \(a\) 中选择了 \(i\) 个数,则 \(b\) 中选择了 \(q_i\) 个数。因为 \(q_i\) 关于 \(i\) 单增,所以可二分确定最优的 \(i\)。同理交换 \(a\)\(b\) 的地位再做一次二分。

const int N = 2e5+5;
int n, m, q, a[N], b[N], x, y, z, px[N], py[N];
ll sa[N], sb[N];
void solve(){
    cin >> n >> m >> q;
    for (int i=1; i<=n; ++i) cin >> a[i], a[i] = -a[i];
    for (int i=1; i<=m; ++i) cin >> b[i], b[i] = -b[i];
    sort(a+1, a+n+1); sort(b+1, b+m+1);
    for (int i=1; i<=n; ++i) sa[i] = sa[i-1] + a[i];
    for (int i=1; i<=m; ++i) sb[i] = sb[i-1] + b[i];
    for (int i=1; i<=n; ++i) px[i] = lower_bound(b+1, b+m+1, a[i]) - b;
    for (int i=1; i<=m; ++i) py[i] = lower_bound(a+1, a+n+1, b[i]) - a;
    while (q--)
    {
        cin >> x >> y >> z;
        int l = 0, r = x, o = 0;
        while (l <= r)
        {
            int mid = (l+r) / 2;
            if (mid + min(y, px[mid]) <= z) o = mid, l = mid+1;
            else r = mid-1;
        }
        ll ans = sa[o] + sb[z-o];
        l = 0, r = y, o = 0;
        while (l <= r)
        {
            int mid = (l+r) / 2;
            if (mid + min(x, py[mid]) <= z) o = mid, l = mid+1;
            else r = mid-1;
        }
        ans = min(ans, sb[o] + sa[z-o]);
        cout << -ans << '\n';
    }
}

int main(){
    ios::sync_with_stdio(false);cin.tie(0);
    int T=1; cin>>T;
    //while(T--)cout<<(solve()?"YES":"NO")<<'\n';
    while(T--)solve();
}

F. Rada and the Chamomile Valley

题意

给一张图,对每个点 \(v\),求所有 \(1\)\(n\) 的必经边中,与 \(v\) 距离最近的边的编号。边 \((x,y)\)\(v\) 的距离定义为端点 \(x\)\(y\)\(v\) 的距离的较小值。若有多个距离最近的边,输出编号最小的。

题解

强行二合一题。

第一部分是割边板子。先边双缩点,然后在树上 dfs 找到 \(1\) 所在 bcc 和 \(n\) 所在 bcc 之间的路径,路径上的边对应原图的割边就是所有的必经边。

第二部分是多源最短路,bfs 即可。由于要编号最小所以用优先队列保证编号小的边能先更新。

const int N = 2e5+5;
vector <int> e[N], e1[N];
void adde(vector <int> * e, int x, int y)
{
    e[x].push_back(y);
}

int n, m, q, x[N], y[N], z;
int tot, dfn[N], low[N], top, sta[N];
int cnt, bel[N];
void dfs(int u, int f)
{
	dfn[u] = low[u] = ++tot, sta[++top] = u;
    for (int v : e[u]) if (v^f)
    {
		if (!dfn[v]) dfs(v, u), low[u] = min(low[u], low[v]);
		else low[u] = min(low[u], dfn[v]);
	}
	if (low[u] == dfn[u])
    {
		++cnt;
		do { bel[sta[top]] = cnt; } while (sta[top--] != u);
	}
}

int pre[N], ans[N];
bool vis[N];
void dfs1(int u, int f)
{
    pre[u] = f;
    for (int v : e1[u]) if (v^f)
        dfs1(v, u);
}

struct node
{
    int dep, ind, u;
    bool operator < (const node & p) const
    {
        return dep > p.dep || dep == p.dep && ind > p.ind;
    }
};

void solve()
{
    cin >> n >> m;
    tot = top = cnt = 0;
    for (int i=1; i<=n; ++i) e[i].clear(), dfn[i] = low[i] = bel[i] = 0;
    for (int i=1; i<=m; ++i)
    {
        cin >> x[i] >> y[i];
        adde(e, x[i], y[i]), adde(e, y[i], x[i]);
    }
    cin >> q;
    dfs(1, 0);
    if (bel[1] == bel[n])
    {
        while (q--) cin >> z, cout << "-1 ";
        cout << '\n';
        return;
    }
    for (int i=1; i<=n; ++i) e1[i].clear(), ans[i] = vis[i] = 0;
    for (int i=1; i<=m; ++i)
    {
        int xx = bel[x[i]], yy = bel[y[i]];
        if (xx == yy) continue;
        adde(e1, xx, yy), adde(e1, yy, xx);
    }
    dfs1(bel[1], 0);
    int z = bel[n];
    while (z != bel[1]) vis[z] = 1, z = pre[z];
    vis[bel[1]] = 1;
    priority_queue <node> pq;
    for (int i=1; i<=m; ++i)
    {
        int xx = bel[x[i]], yy = bel[y[i]];
        if (xx == yy || !vis[xx] || !vis[yy]) continue;
        pq.push((node){0, i, x[i]});
        pq.push((node){0, i, y[i]});
    }
    while (!pq.empty())
    {
        auto cur = pq.top();
        pq.pop();
        if (ans[cur.u]) continue;
        ans[cur.u] = cur.ind;
        for (int v : e[cur.u]) if (!ans[v]) pq.push((node){cur.dep+1, cur.ind, v});
    }
    while (q--) cin >> z, cout << ans[z] << ' ';
    cout << '\n';
}
posted @ 2025-08-22 02:11  EssnSlaryt  阅读(185)  评论(1)    收藏  举报