*题解:P10408 「SMOI-R1」Apple
解析
考虑没有修改怎么做,发现是一个裸的子集和,跑一遍 SOSDP 即可在总共 \(O(n2^n)\) 时间内求出答案。现在有了修改,枚举 \(S\) 的所有超集进行修改即可,单次修改时间复杂度 \(O(2 ^ n)\)。
我们发现,这个做法单次查询时间复杂度是 \(O(1)\) 的,单次修改时间复杂度是 \(O(2 ^ n)\) 的,那么能不能平衡一下呢?注意到这个做法维护的是子集和数组,那么我们整个新做法维护原始数组。修改时在原始数组上修改,这是 \(O(1)\) 的,求子集和时枚举子集,这是 \(O(2^n)\) 的。考虑平衡,设 \(f_{S}\) 表示所有前 \(\lceil\frac{n}{2}\rceil\) 位为 \(S\) 的前 \(\lceil\frac{n}{2}\rceil\) 位的子集,后 \(\lfloor\frac{n}{2}\rfloor\) 位为 \(S\) 的后 \(\lfloor\frac{n}{2}\rfloor\) 位的编号,其对应苹果的价值和。询问时,\(S\) 的前 \(\lceil\frac{n}{2}\rceil\) 位不动,枚举后 \(\lfloor\frac{n}{2}\rfloor\) 位的子集;修改时,\(S\) 的后 \(\lfloor\frac{n}{2}\rfloor\) 位不动,枚举前 \(\lceil\frac{n}{2}\rceil\) 位的超集。
时间复杂度 \(O(q2^{\frac{n}{2}})\)。
代码
/*
*/
#include <bits/stdc++.h>
#define eps 0.0000000001
#define ls(x) ((x) << 1)
#define rs(x) (((x) << 1) | 1)
//#define mid ((l + r) >> 1)
using namespace std;
typedef long long ll;
typedef unsigned ui;
typedef pair<int,int> pii;
const int N = (1 << 20) + 5,M = 20 + 5,P = 2000000,mod = 1e9 + 7,mod2 = 1e9 + 7,b1 = 131,b2 = 13331;
ll v[N],f[N],g[N];
int read(){
int a = 1,x = 0;
char ch = getchar();
while(ch > '9' || ch < '0'){
if(ch == '-') a = -1;
ch = getchar();
}
while(ch <= '9' && ch >= '0'){
x = x * 10 + ch - '0';
ch = getchar();
}
return a * x;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int n,q;
n = read();
q = read();
int m1 = n / 2,m2 = (n + 1) / 2;
int x = (1 << m1) - 1;
for(int i=0;i<(1 << n);i++){
v[i] = read();
g[i] += v[i];
f[i] += v[i];
}
for(int i=m1;i<n;i++){
for(int j=0;j<(1 << n);j++){
if(j & (1 << i)){
f[j] += f[j ^ (1 << i)];
}
}
}
while(q--){
int op,s,a;
op = read();
s = read();
if(op == 1){
ll res = 0;
for(int i=0;i<=(s & x);i++){
int j = ((((s >> m1) << m1)) | i);
if((i & s) == i){
res += f[j];
}
}
cout<<res<<'\n';
}else{
a = read();
int t = s & x;
for(int i=0;i<(1 << m2);i++){
int j = t | (i << m1);
if((j | s) == j){
f[j] += a - v[s];
}
}
v[s] = a;
}
}
return 0;
}

浙公网安备 33010602011771号