三十年河东,三十年河西|

自动机

园龄:1年10个月粉丝:2关注:4

codeforces round 895 (div. 3)

E

题意:

给你一个整数数组a, 以及一个二进制字符串s, 现在有三种操作:

  1. 对于给定的区间[l, r],你需要反转s[l, r];
  2. 输出所有s[i] = 1的a[i]异或值
  3. 输出所有s[i] = 0的a[i]异或值

思路 1:

暴力做法是O(q * n)超时,想到使用差分来简化操作1,但最坏的情况下复杂度依然是O(q * n);
需要用到一个性质,想要反转二进制区间[l, r],只需要异或上这个区间,比如 [1010] ^ [1010] = [0101]
所以对于操作1,只需要求一遍异或前缀和,然后O(1)解决,操作2,3也类似:如果已知一段区间[l, r]的操作2的异或值为x, 那么 x ^= s[l - 1] ^ s[r]等价于将这段区间里s[i] = 1的值删除然后加上s[i] = 0
因为两个相同的数异或等于0,就相当于把它从区间删除

思路 2:

利用线段树.....

inline void solve() 
{
    int n; cin >> n;
    std::vector<int> a(n + 1);
    std::vector<int> sum(n + 1);
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        sum[i] = sum[i - 1] ^ a[i];
    }

    string s; cin >> s;
    int ans = 0; // 所有1的异或值
    for (int i = 1; i <= n; i++)
    {
        if (s[i - 1] == '1') ans ^= a[i];
    }
    int q; cin >> q;
    while (q--)
    {
        int op; cin >> op;
        if (op == 1)
        {
            int l, r; cin >> l >> r;
            ans ^= sum[l - 1] ^ sum[r];
        }
        else
        {
            int g; cin >> g;
            if (g == 1) cout << ans << ' ';
            else cout << (ans ^ sum[n]) << ' ';
        }
    }

    cout << endl;
}

F:

题意:

你有n个小动物, 每个小动物都有一个售价和一个害怕的动物,a[i]表示 i 害怕 a[i];

  1. 如果 i 在 a[i] 之前卖出那么你可以得到 2 * c[i] 的报酬
  2. 反之你可以获得 c[i] 的报酬
    请确定一个售卖顺序使得获得报酬最多

思路:

可以把害怕关系连成个图(i, a[i])连有向一条边,为了获得最多的报酬,应该按照topsort的顺序卖,但是图可能存在环但是topsort解决不了环的问题,所以必须把环拆开
优先把售价最小的那条边消掉,这样的话报酬就能最大

inline void solve() 
{
    int n; cin >> n;
    std::vector<int> a(n + 1);
    std::vector<int> c(n + 1);
    std::vector<int> d(n + 1);
    std::vector<bool> st(n + 1);
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        d[a[i]]++;
    }
    std::vector<int> ans;
    for (int i = 1; i <= n; i++)
        cin >> c[i];
    // 先按topsort卖
    queue<int> q;
    for (int i = 1; i <= n; i++)
    {
        if (d[i] == 0) q.push(i);
    }
    while (!q.empty())
    {
        int t = q.front();
        q.pop();
        ans.pb(t);
        st[t] = true;
        if (--d[a[t]] == 0) q.push(a[t]);
    }

    for (int i = 1; i <= n; i++)
    {
        if (!st[i])
        {
            int idx1 = a[i];
            int idx = i;
            while (idx != idx1) // 找到环里面售价最小的那个
            {
                if (c[idx1] < c[idx]) idx = idx1;
                idx1 = a[idx1]; 
            }

            int k = a[idx]; // 从最小值的下一个开始找,就能让最小值最后卖出
            while (!st[k])
            {
                ans.pb(k);
                st[k] = true;
                k = a[k];
            }
        }
    }

    for (int i = 0; i < n; i++)
        cout << ans[i] << " \n"[i == n - 1];

}

G

题意:

已知一个正整数序列a, 你需要选择一个区间[l, r]将这段区间替换成区间的乘积,要求替换后序列和最大

思路:

对于乘积来说选择a[i] = 1是不划算的因为不仅不会增加乘积还会浪费一个1,所以1肯定不能作为端点,所以直观来说可以选择第一个非1点,和最后一个非1点,作为端点
但是也可能中间有1,所以需要判断一下乘积是不是大于和,最坏的情况下大于2e14就可以了,如果不行的话就暴力O(n * n)选择端点,大于1的个数最多有50(250 > 2e14)个所以暴力没问题

inline void solve() 
{
    int n; cin >> n;
    std::vector<int> a(n + 1);

    for (int i = 1; i <= n; i++)
        cin >> a[i];

    int l = 1, r = n;
    while (a[l] == 1 && l <= r) l++;
    while (a[r] == 1 && r >= l) r--;

    if (l > r) // 没有1
    {
        cout << "1 1" << endl;
        return;
    }

    __int128 res = 1;
    bool flag = false;
    for (int i = l; i <= r; i++)
    {
        if (res * (__int128)a[i] > 2e14)
        { 
            flag = true;
            break;
        }
        res *= (__int128)a[i];
    }

    if (flag)
    {
        cout << l << ' ' << r << endl;
        return;
    }

    std::vector<LL> s1(n + 1), s2(n + 1);
    std::vector<int> p;
    s2[0] = 1;
    for (int i = 1; i <= n; i++)
    {
        if (a[i] != 1) p.pb(i);
        s1[i] = s1[i - 1] + a[i];
        s2[i] = s2[i - 1] * a[i];
    }

    LL ans = accumulate(a.begin(), a.end(), (LL)0);

    PII op = make_pair(1, 1);

    for (int i = 0; i < p.size(); i++)
    {
        for (int j = i; j < p.size(); j++)
        {
            int l = p[i], r = p[j];
            LL t = s2[r] / s2[l - 1] + s1[n] - (s1[r] - s1[l - 1]);
            if (t > ans)
            {
                ans = t;
                op = make_pair(l, r);
            }
        }
    }
    cout << op.first << ' ' << op.second << endl;
}

本文作者:自动机

本文链接:https://www.cnblogs.com/monituihuo/articles/17693866.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   自动机  阅读(3)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起