*题解:P10045 [CCPC 2023 北京市赛] 线段树
解析
根据题意,\(a_i\) 在任意时刻都是奇数。也就是说 \(b_i=a_i - 1\) 在任意时刻都是偶数。对于 \(\prod_{i \in [l,r]}(b_i + 1)\),由于模数是 \(2^{20}\),且该式展开后任意含有大于等于 \(20\) 个 \(b_i\) 的项,都含有因子 \(2^{20}\),故它们取模后都会变成 \(0\)。故考虑在线段树上维护 \(f_i\) 表示含有 \(i\) 个 \(b_i\) 的项的乘积,最终答案是 \(\sum_{i=0}^{19}f_i\)。
考虑怎么合并 \(f\),直接枚举左儿子含有多少个 \(b\) 的项:
\(f^{(L)},f^{(R)}\) 分别为左右儿子的 \(f\)。
考虑怎么修改,即求 \(x\) 对 \(f\) 的贡献。考虑组合意义,每轮可以视为有 \(a_i\) 个本质不同的深色球和 \(x\) 个本质不同浅色球供选取,最终结果就是选取的方案数总和,这样修改前的 \(f_i\) 就表示选了 \(i\) 个深色球的方案数。考虑修改之后选了 \(k\) 个球的方案 \(g_k\),,枚举深色球数量 \(i\),那么浅色球数量就是 \(k - i\),有:
其中 \(len\) 为当前区间长度,\(C_{len - i}^{k - i}\) 统计的是在总共的 \(len - i\) 次选择机会(选深色球已经消耗了 \(i\) 次机会)中消耗 \(k-i\) 次选择机会选浅色球。暴力计算即可。
考虑怎么求答案,假设已经知道左右儿子的答案(乘积),那么直接乘起来就好。
时间复杂度 \(O((q \log n)M^2)\),\(M\) 为 \(20\)。
卡常小技巧。由于模运算有性质:当 \(m∣n\) 时,\((a\bmod n)\bmod m=a\mod m\)。故 \((a \bmod 2^{30})\bmod 2^{20}=a\bmod 2^{20}\),故可以利用 unsigned 的自然溢出来减少取模次数。
代码
/*
*/
#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 = 2e5 + 5,M = 20,P = 2000000,mod = 1 << 20,mod2 = 1e9 + 7,b1 = 131,b2 = 13331;
ui a[N],f[N << 2][M],tag[N << 2],C[N][M],mi[M];
int n,q;
void push_up(int p){
for(int i=0;i<M;i++){
f[p][i] = 0;
for(int j=0;j<=i;j++){
f[p][i] += f[ls(p)][j] * f[rs(p)][i - j];
}
}
}
void build(int p,int l,int r){
if(l == r){
f[p][0] = 1;
f[p][1] = a[l];
return;
}
build(ls(p),l,mid),build(rs(p),mid + 1,r);
push_up(p);
}
void add_tag(int p,int l,int r,int x){
int len = r - l + 1;
tag[p] += x;
for(int i=1;i<M;i++){
mi[i] = mi[i - 1] * x;
}
ui tmp[M];
memcpy(tmp,f[p],sizeof(tmp));
for(int i=0;i<min(M,len + 1);i++){
f[p][i] = 0;
for(int j=0;j<=i;j++){
f[p][i] += tmp[j] * C[len - j][i - j] * mi[i - j];
}
}
}
void push_down(int p,int l,int r){
if(!tag[p]) return;
add_tag(ls(p),l,mid,tag[p]);
add_tag(rs(p),mid + 1,r,tag[p]);
tag[p] = 0;
}
void add(int p,int l,int r,int L,int R,int x){
if(l > R || r < L) return;
if(l >= L && r <= R){
add_tag(p,l,r,x);
return;
}
push_down(p,l,r);
add(ls(p),l,mid,L,R,x),add(rs(p),mid + 1,r,L,R,x);
push_up(p);
}
ui ask(int p,int l,int r,int L,int R){
if(l > R || r < L) return 1;
if(l >= L && r <= R){
ui res = 0;
for(int i=0;i<M;i++){
res += f[p][i];
}
return res;
}
push_down(p,l,r);
return ask(ls(p),l,mid,L,R) * ask(rs(p),mid + 1,r,L,R);
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
mi[0] = 1;
cin>>n>>q;
for(int i=0;i<=n;i++){
C[i][0] = 1;
}
for(int i=1;i<=n;i++){
for(int j=1;j<M;j++){
C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
}
}
for(int i=1;i<=n;i++){
cin>>a[i];
a[i]--;
}
build(1,1,n);
while(q--){
int op,l,r,x;
cin>>op>>l>>r;
if(op == 1){
cin>>x;
add(1,1,n,l,r,x);
}else{
ui res = ask(1,1,n,l,r);
cout<<res % mod<<"\n";
}
}
return 0;
}

浙公网安备 33010602011771号