22.8.10
22.8.10
CF1716E
题意: 给一个长度为 \(2^n\) 的数组, 需要回答 \(q\) 个询问, 每个询问给一个 \(k\), 此时需要对数组做操作, 对于\(i\in[1,2^n-2^k]\), 按照升序进行操作, 将 \(a_i\) 与 \(a_{i+2^k}\) 交换, 若到一个 \(i\) 时, 它已经进行过一次操作, 则跳过这次交换, 问交换后最大子段和是多少 (可以为空段)
思路:
① 首先考虑 \(k\) 的顺序有没有关系, 发现对于每个 \(k\) , 其对答案的影响与出现顺序没有关系, 只与其出现次数的奇偶性有关系, 若出现偶数次, 则对答案没有影响, 出现奇数则会出现一次交换
② 考虑状态数量, 对于一个长度为 \(2^n\) 的数组, 其状态数只有 \(2^n\) 种, 可以用二进制理解一下,发现结论是显然的
那么此时我们就可以考虑预处理出每种状态对应的答案
考虑暴力枚举, 复杂度是 \(2^{2n}\) 的, 显然不能通过此题
其实其中很多状态可以给后面直接贡献, 也就是只用算一次, 那么我们考虑分治, 为了求整个数组的答案, 我们可以先把它分为两个长度相等的子区间, 然后递归计算即可, 此时最多分 \(log\) 层, 即 n层, 每层处理的复杂度即为其状态数, 即 \(2^n\), 那么总复杂度为 \(O(2^n*n)\)
具体实现可以直接用线段树来写
//@Author: ZI_MA
#include<bits/stdc++.h>
using namespace std;
#define IOS ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define PP pair<int, int>
#define all(x) x.begin()+1,x.end()
#define endl '\n'
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
const int INF=1e16;
struct node {
int lm,rm,sum,mx;
node(int a=0,int b=0,int c=0,int d=0):lm(a),rm(b),sum(c),mx(d) {}
void update(const node &x,const node &y) {
lm=max(x.lm,x.sum+y.lm);
rm=max(y.rm,y.sum+x.rm);
sum=x.sum+y.sum;
mx=max({x.mx,y.mx,x.rm+y.lm});
}
};
vector<node> f[1<<22];
int a[1<<22];
void build(int p,int l,int r,int k) {
f[p].resize(r-l+1);
if(l==r) {
int tmp=max(0ll,a[l]);
f[p][0]=node{tmp,tmp,a[l],tmp};
return;
}
int mid=(l+r)>>1;
build(ls(p),l,mid,k-1);
build(rs(p),mid+1,r,k-1);
for(int i=0;i<r-l+1;i++) {
if(i>>k&1)
f[p][i].update(f[rs(p)][i^(1<<k)],f[ls(p)][i^(1<<k)]);
else
f[p][i].update(f[ls(p)][i],f[rs(p)][i]);
}
}
signed main() {
IOS;
int n;
cin>>n;
for(int i=1;i<=(1<<n);i++) {
cin>>a[i];
}
build(1,1,1<<n,n-1);
int q;
cin>>q;
int now=0;
while(q--) {
int k; cin>>k;
now^=(1<<k);
cout<<f[1][now].mx<<endl;
}
return 0;
}

浙公网安备 33010602011771号