CF1716E(分治,最大子段和,dp)

CF1716E(分治,最大子段和,dp)

题意

给一个序列 a ,长 2n

现在有 q 次询问,每次给出一个 k 。之后要将 aaiai+2k 交换,每个元素最大进行一次交换,i+2k>n 时不进行交换。

输出操作后 a 的最大子段和。

每次操作 不独立

思路

首先,得有一个前置知识,分治法求最大子段和。

通过维护一段子区间的 ls,s,rs,sum (分别是前缀最大和,最大子段和,后缀最大和,区间和)。可以 pushup 合并出区间答案。


  1. 两次操作会相互抵消,并且与操作顺序无关。

因此,所有的答案可能一共就 2n 种可能。我们用一串 n 位01串表示操作。

最暴力的解法就是对每种状态dfs一遍搜出答案,单次复杂度 O(2n) ,会T。

  1. 该序列可以建立一棵完全二叉树,某一位的操作相当于交换这棵树某层的左右子树,而不会影响上层。

可以发现,这保证了 无后效性,考虑dp处理答案。

定义 f[i][j] 表示以 i 为根,i 位于第 m 层,考虑操作序列低 m 位为 j 的答案。于是 f[1][j] 就是答案。

考虑转移,设 i 的左右儿子为 lc,rc

如果第 m+1 层不换

f[i][j]=f[lc][j]f[rc][j]

如果交换

f[i][j]=f[rc][j]f[lc][j]

其中 是结点合并:

struct Node {
        int ls, s, rs, sum;
        Node operator+(const Node& rv) const {
            return {max(ls,sum + rv.ls),
            max({s,rv.s,rs + rv.ls}),
            max(rv.rs,rs + rv.sum),
            sum + rv.sum};
        }
    };

于是,我们做一个树dp就可以预处理出答案。时间复杂度 O(n2n)

最后一问题是空间开销太大,将 f[i][j] 的第一维压掉用一个滚动数组即可。在递归上的实现方式是递归返回vector,具体看代码。

#include<bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3fll
#define ull unsigned long long
#define endl '\n'
#define int long long
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
using namespace std;
mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}
using PII = array<int,2>;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;

void solve()
{    
    int n; cin >> n;
    n = 1 << n;
    vector<int>a(n + 1);
    for(int i = 1;i <= n;i += 1)
        cin >> a[i];
    
    struct Node {
        int ls, s, rs, sum;
        Node operator+(const Node& rv) const {
            return {max(ls,sum + rv.ls),
            max({s,rv.s,rs + rv.ls}),
            max(rv.rs,rs + rv.sum),
            sum + rv.sum};
        }
    };

    function<vector<Node>(int,int)> dfs = [&](int l,int r) {
        if(l == r) {
            return vector<Node>{{a[l],a[l],a[l],a[l]}};
        }
        int mid = l + r >> 1;
        auto resL = dfs(l,mid);
        auto resR = dfs(mid + 1,r);

        int k = 2 * resL.size();
        vector<Node> res(k);
        for(int i = 0;i < resL.size();i += 1) {
            res[i] = resL[i] + resR[i];
            res[i + k / 2] = resR[i] + resL[i];
        }
        return res;
    };

    auto ans = dfs(1,n);

    int q, idx = 0; cin >> q;
    while(q --) {
        int t; cin >> t;
        idx ^= (1 << t);
        cout << max(0ll, ans[idx].s) << endl;
    }

}
signed main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

    //int T;cin>>T;
    //while(T--)
        solve();

    return 0;
}
posted @   Mxrurush  阅读(49)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示