CF1716E(分治,最大子段和,dp)
CF1716E(分治,最大子段和,dp)
题意
给一个序列 \(a\) ,长 \(2^n\)。
现在有 \(q\) 次询问,每次给出一个 \(k\) 。之后要将 \(a\) 中 \(a_i\) 和 $a_{i+2^{k}} $ 交换,每个元素最大进行一次交换,\(i+2^k>n\) 时不进行交换。
输出操作后 \(a\) 的最大子段和。
每次操作 不独立。
思路
首先,得有一个前置知识,分治法求最大子段和。
通过维护一段子区间的 \(ls,s,rs,sum\) (分别是前缀最大和,最大子段和,后缀最大和,区间和)。可以 \(pushup\) 合并出区间答案。
- 两次操作会相互抵消,并且与操作顺序无关。
因此,所有的答案可能一共就 \(2^n\) 种可能。我们用一串 \(n\) 位01串表示操作。
最暴力的解法就是对每种状态dfs一遍搜出答案,单次复杂度 \(O(2^n)\) ,会T。
- 该序列可以建立一棵完全二叉树,某一位的操作相当于交换这棵树某层的左右子树,而不会影响上层。
可以发现,这保证了 无后效性,考虑dp处理答案。
定义 \(f[i][j]\) 表示以 \(i\) 为根,\(i\) 位于第 \(m\) 层,考虑操作序列低 \(m\) 位为 \(j\) 的答案。于是 \(f[1][j]\) 就是答案。
考虑转移,设 \(i\) 的左右儿子为 \(lc,rc\) 。
如果第 \(m+1\) 层不换
\[f[i][j]=f[lc][j]\oplus f[rc][j]
\]
如果交换
\[f[i][j]=f[rc][j] \oplus f[lc][j]
\]
其中 \(\oplus\) 是结点合并:
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(n*2^n)\)
最后一问题是空间开销太大,将 \(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;
}