首页 > 其他分享 >[题解] Codeforces Round 895 (Div. 3) F~G

[题解] Codeforces Round 895 (Div. 3) F~G

时间:2023-09-09 17:56:13浏览次数:32  
标签:std 895 int 题解 back ++ vector auto Div

Codeforces Round 895 (Div. 3) F~G

F. Selling a Menageri

考虑如何让卖出的价格翻倍,那么自然是从 \(i \to a_i\) 。通过这样连边,我们可以发现,边集构成了基环树森林。显而易见的是,如果不考虑环,那么图就是拓扑图,按照拓扑关系跑一遍,就可以使得所有点价值最多。

现在考虑环上的问题。如果出现一个环,那么会出现循环的问题:我们从哪里开始?因为这里形成了一个简单环,那么说明这个环上需要有一个不能被满足翻倍价格,也就是让其中一个点的前驱优先实现,则此时该边在图上不存在,该基环树变成有向无环图,此时可以通过拓扑排序得到序列。

问题是我们要如何确定让哪个点不被翻倍呢?贪心的想,让环中点权值最小的点不被翻倍即可,即断开在这个环中连向这个权值最小的边断开,即可。

最后,我们只需要对处理后的图跑一遍拓扑,即可得到答案序列。

这里处理环使用了强连通分量找环,写得比较麻烦。

void Main() {
    int n;
    std::cin >> n;

    std::vector<int> a(n);
    for (auto &item : a) {
        std::cin >> item;
        item --;
    }
    std::vector<int> vw(n);
    for (auto &item : vw) {
        std::cin >> item;
    }

    std::vector adj(n, std::vector<int>{});
    for (int i = 0; i < n; i ++) {
        adj[i].emplace_back(a[i]);
    }

    std::vector<int> dfn(n, -1), low(n, -1), scc_id(n, -1);
    std::vector<std::vector<int>> scc;
    int scc_cnt = 0;
    std::vector<int> stk;
    std::vector<bool> inst(n);

    auto tarjan = [&, stamp{0}](auto &self, int from) mutable -> void {
        dfn[from] = low[from] = stamp ++;
        stk.emplace_back(from);
        inst[from] = true;

        for (auto to : adj[from]) {
            if (dfn[to] == -1) {
                self(self, to);
                low[from] = std::min(low[from], low[to]);
            } else if (inst[from]) {
                low[from] = std::min(low[from], dfn[to]);
            }
        }

        if (dfn[from] == low[from]) {
            int v;
            scc.emplace_back(std::vector<int>{});
            do {
                v = stk.back();
                stk.pop_back();
                inst[v] = false;
                scc_id[v] = scc_cnt;
                scc.back().emplace_back(v);
            } while (v != from);
            scc_cnt ++;
        }
    };

    for (int i = 0; i < n; i ++) {
        if (dfn[i] == -1) {
            tarjan(tarjan, i);
        }
    }

    std::vector<bool> fuk(n);
    for (auto &list : scc) {
        std::ranges::sort(list, std::less{}, [&](auto x) { return vw[x]; });
        if (list.size() >= 2) {
            fuk[list.front()] = true;
        }
    }

    std::vector radj(n, std::vector<int>{});
    std::vector<int> ind(n);

    for (int i = 0; i < n; i ++) {
        if (!fuk[i]) {
            radj[i].emplace_back(a[i]);
            ind[a[i]] ++;
        }
    }

    std::vector<int> ans;
    ans.reserve(n);
    for (int i = 0; i < n; i ++) {
        if (ind[i] == 0) {
            ans.emplace_back(i);
        }
    }

    for (int i = 0; i < std::min<int>(n, ans.size()); i ++) {
        int from = ans[i];
        for (auto to : radj[from]) {
            if (-- ind[to] == 0) {
                ans.emplace_back(to);
            }
        }
    }

    assert((int)ans.size() == n);
    for (auto item : ans) {
        std::cout << item + 1 << ' ';
    }
    std::cout << '\n';
}

G. Replace With Product

因为 \(1\) 与其他数相乘后的结果会使得总和减少 \(1\) ,所以我们要避免过多的 \(1\) 被处理。因此,首先我们可以将两边多余的 \(1\) 先排除掉。

排除掉之后,我们仍然无法确定中间存在的 \(1\) 是否会对答案存在影响。考虑到相乘大小膨胀非常大,因此我们可以预处理这个范围内的相乘结果,如果比和的最大结果还大,那么可以直接选择这段区域。

如果还是不能确定是不是,还是由于相乘大小增长非常大,假设剩下的大于 \(1\) 的数字都是 \(2\) ,那么其一定不会太多,否则相乘结果非常大,满足之前的结论。

因为这些零散的数字比较少,我们直接两重循环遍历求解即可。

void Main() {
    int n;
    std::cin >> n;
    
    std::vector<i64> a(n);
    for (auto &item : a) {
        std::cin >> item;
    }
    
    if (std::ranges::count(a, 1) == n) {
        std::cout << 1 << ' ' << 1 << '\n';
        return;
    }
    
    int l = 0, r = n - 1;
    while (a[l] == 1) {
        l ++;
    }
    while (a[r] == 1) {
        r --;
    }

    i64 tot = 1;
    for (int i = l; i <= r; i ++) {
        tot *= a[i];
        if (tot > 1e9) {
            std::cout << l + 1 << ' ' << r + 1 << '\n';
            return;
        }
    }

    std::vector<int> pos;
    for (int i = l; i <= r; i ++) {
        if (a[i] > 1)
            pos.emplace_back(i);
    }

    tot = reduce(a.begin(), a.end());
    auto pre_ret = tot;
        
    int L = 0, R = 0;
    for (int i = 0; i < pos.size(); i ++) {
        i64 m = 1, s = 0;
        for (int j = i; j < pos.size(); j ++) {
            int l = pos[i], r = pos[j];
            m *= a[r];
            s += a[r] - 1;
            auto ret = tot - (r - l + 1) - s + m;

            if (pre_ret < ret) {
                pre_ret = ret;
                L = l;
                R = r;
            }
        }
    }

    std::cout << L + 1 << ' ' << R + 1 << '\n';
}

标签:std,895,int,题解,back,++,vector,auto,Div
From: https://www.cnblogs.com/FlandreScarlet/p/17689908.html

相关文章

  • [AGC036C] GP 2 题解
    洛谷题目链接AT原题考虑构造出来的序列\(a\)的特征,因为每次会给\(a_i\)加\(1\),\(a_j\)加\(2\),所以每次操作后\(\suma_i\)会加上\(3\)。所以有\(\suma_i=3m\)。又因为每次操作只给一个数加\(1\),所以每次操作要么给序列加入一个奇数,要么使原来的一个奇数变成偶数......
  • SMU Autumn 2023 Round 1(Div.1)
    SMUAutumn2023Round1(Div.1)A.SetorDecrease(枚举)题意就是你可以进行两种操作,将\(a_i-1\)或者令\(a_i\)等于\(a_j\),然后使得\(\sum\limits_{i=1}^{n}a_i\leqk\),求最少的操作步数首先我们让一个大数变成一个最小数的贡献肯定是要比让大数减一产生的贡献更多,所以我......
  • [题解] CF29D Ant on the Tree
    CF29DAntontheTree题目知识点:LCA。题目传送门题意给定一棵以\(1\)为节点的树,再给定树的所有叶子节点的一个序列。现在执行一个操作:从\(1\)开始遍历每个节点,并返回根,要求每条边经过的次数一定为\(2\)。问是否能够使得访问节点序列中叶子节点的序列符合给定序列的条......
  • P2206题解
    题目大意:给定一些指令,计算需要多大的舞台。这是一道大模拟!!!只要遍历每次指令,然后判断是否摔倒,摔倒输出`-1`否则记录,最后求出面积就行了。最后附上代码1#include<bits/stdc++.h>2usingnamespacestd;3constintxx[]={-1,0,1,0},yy[]={0,1,0,-1};//不同......
  • CF1513C题解
    一道递推由于对于一个数x,可得x+10-x=10(废话)于是问题就变成了0+m次,然后x+m就变成0+x+m(还是废话)于是可以写一个递推。首先对于函数f(m)可分为m ≤9和m>9,然后可得出递推式结果为1或f(m-9)+f(m-10),所以我们可以直接求出结果再分解数位求值。最后贴上代码......
  • P2203题解
    题意给定一个环形01序列,每次变化时,对于每个位置,如果前一个值是1,则当前值翻转。求变化B次后的序列。思路由于B的值很大,所以如果对每一次变化进行模拟,效率非常低下。不难发现,每一次变化后的状态完全是由当前状态决定的,而N的范围很小,所以可能的状态总数2^N也不是很大......
  • CF812B题解
    康了康唯一的题解,说没必要用DP,我就给出DP的解法。这其实是道水题,唯一的坑是有可能楼上没有开的灯,坑了我们机房的一堆人(WAontest4),剩下的就是DP。我们用a[n][0],表示第n层的第一个房间,用a[n][1],表示第n层的最后一个房间。这里提供一个收集型的写法。所以可得状态转移......
  • CF1690E题解
    ##主要题意:有$n$个礼物,要两两合并,然后除以$k$最后求和最大。##思路:先加入每个数除以$k$的商(单独组成$k$的个数),然后全部$\bmod\k$存入数组,排序,最后双指针一个前一个后求两个余数可以大于等于$k$的两个礼物。##代码:```cpp#include<bits/stdc++.h>usingname......
  • CF1690C题解
    ##主要题意:>有$n$个任务,必须在$s_i$到$t_i$之间完成,求每个任务最大可以完成多久(优先前面的最大)。##思路>就拿一个变量记录当前时间,然后贪心选择$a[i].t$和$a[i+1].t$中的最小值,(应为至少也要给下一个任务留$1$的时间),最后做减法,输出即可。##代码>```cpp>#incl......
  • pytest运行警告问题解决:DeprecationWarning: pkg_resources is deprecated as an API
    前言最近在运行pytest的时候,经常出现这个警告DeprecationWarning:pkg_resourcesisdeprecatedasanAPISeehttps://setuptools.pypa.io/en/latest/pkg_resources.htmlfrompkg_resourcesimportiter_entry_points从警告上看是方法被弃用,肯定是因为新版弃用了旧版的语法。......