题解:P16389 [IATI 2024] Five
lailai0916
·
·
题解
题意简述
一个五阶线性递推数列的下标可以为任意整数。维护整数数组 a 的区间加法,并查询区间内 x_{a_i} 的和,答案对 M=10^8+543 取模。
解题思路
递推的特征多项式为:
P(t)=t^5-5t^4-4t^3-3t^2-2t-1
| $j$ | $r_j$ | $c_j$ |
| :-: | :-: | :-: |
| $0$ | $82181655$ | $13944628$ |
| $1$ | $69727867$ | $37815043$ |
| $2$ | $36684046$ | $51211683$ |
| $3$ | $10002474$ | $92161528$ |
| $4$ | $1405049$ | $4868204$ |
这些常数满足:
$$
P(t)\equiv\prod_{j=0}^4(t-r_j)\pmod M
$$
对每个 $0\le k\le4$,还满足:
$$
\sum_{j=0}^4c_jr_j^k\equiv k\pmod M
$$
逐个枚举模 $M$ 的元素并代入 $P$,可以得到这张根表。再对初值建立五元线性方程组,即可求出系数。实现中直接保存这组常数。上述两个等式提供了可独立复核的判据。
每个 $r_j^k$ 都满足原递推,线性组合仍满足递推。第二个等式说明该组合与 $x_k$ 的前 $5$ 项相同,所以递推唯一性给出:
$$
x_k\equiv\sum_{j=0}^4c_jr_j^k\pmod M
$$
这个结论同样覆盖负下标。原递推中 $x_k$ 的系数为 $1$,已知连续后 $5$ 项时可以唯一反推出 $x_k$。另一方面,所有 $r_j$ 均非零,所以 $r_j^k$ 对负整数 $k$ 也有定义。两侧是同一个双向递推的解。
由费马小定理,非零底数满足 $r_j^{M-1}\equiv1\pmod M$。计算 $r_j^k$ 时,先把任意正负指数 $k$ 归一化到 $[0,M-2]$ 即可。
线段树结点对每个 $0\le j<5$ 维护:
$$
f_j=\sum_{i=l}^rr_j^{a_i}\pmod M
$$
若区间内所有 $a_i$ 增加 $d$,则对应分量满足:
$$
f_j\gets r_j^df_j
$$
因此,懒标记保存 $5$ 个乘数,并按分量相乘。查询区间得到全部 $f_j$ 后,答案为:
$$
\sum_{j=0}^4c_jf_j\pmod M
$$
若每次计算 $r_j^d$ 都使用快速幂,单次修改还会多一个对数。取 $B=10000$,把规范化后的指数写为 $d=uB+v$。预处理 $r_j^v$ 与 $(r_j^B)^u$ 后,一次乘法即可得到 $r_j^d$。
预处理和建树的时间复杂度为 $O(n+\sqrt{M})$。每次操作的时间复杂度为 $O(\log n)$,空间复杂度为 $O(n+\sqrt{M})$。
## 参考代码
```cpp
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=100005;
const int M=400005;
const int B=10000;
const int K=10005;
const int mod=100000543;
const int r[5]={82181655,69727867,36684046,10002474,1405049};
const int c[5]={13944628,37815043,51211683,92161528,4868204};
int a[N],sm[5][B],bg[5][K];
ll Pow(ll x,ll y)
{
x%=mod;
ll res=1;
while(y)
{
if(y&1)res=res*x%mod;
x=x*x%mod;
y>>=1;
}
return res;
}
void init()
{
for(int i=0;i<5;i++)
{
sm[i][0]=1;
for(int j=1;j<B;j++)sm[i][j]=(ll)sm[i][j-1]*r[i]%mod;
bg[i][0]=1;
int x=Pow(r[i],B);
for(int j=1;j<K;j++)bg[i][j]=(ll)bg[i][j-1]*x%mod;
}
}
int power(int i,ll x)
{
x%=mod-1;
if(x<0)x+=mod-1;
int p=x/B,q=x%B;
return (ll)bg[i][p]*sm[i][q]%mod;
}
struct SEG
{
int val[M][5],tag[M][5];
bool vis[M];
void gx(int u,const int p[])
{
for(int i=0;i<5;i++)
{
val[u][i]=(ll)val[u][i]*p[i]%mod;
tag[u][i]=(ll)tag[u][i]*p[i]%mod;
}
vis[u]=1;
}
void push_up(int u)
{
for(int i=0;i<5;i++)val[u][i]=(val[u*2][i]+val[u*2+1][i])%mod;
}
void push_down(int u)
{
if(!vis[u])return;
gx(u*2,tag[u]);
gx(u*2+1,tag[u]);
for(int i=0;i<5;i++)tag[u][i]=1;
vis[u]=0;
}
void build(int u,int l,int r)
{
for(int i=0;i<5;i++)tag[u][i]=1;
vis[u]=0;
if(l==r)
{
for(int i=0;i<5;i++)val[u][i]=power(i,a[l]);
return;
}
int mid=l+r>>1;
build(u*2,l,mid);
build(u*2+1,mid+1,r);
push_up(u);
}
void update(int u,int l,int r,int x,int y,const int p[])
{
if(x<=l&&r<=y){gx(u,p);return;}
push_down(u);
int mid=l+r>>1;
if(x<=mid)update(u*2,l,mid,x,y,p);
if(y>mid)update(u*2+1,mid+1,r,x,y,p);
push_up(u);
}
void query(int u,int l,int r,int x,int y,int res[])
{
if(x<=l&&r<=y)
{
for(int i=0;i<5;i++)res[i]=(res[i]+val[u][i])%mod;
return;
}
push_down(u);
int mid=l+r>>1;
if(x<=mid)query(u*2,l,mid,x,y,res);
if(y>mid)query(u*2+1,mid+1,r,x,y,res);
}
}T;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
init();
int n,q;
cin>>n>>q;
for(int i=1;i<=n;i++)cin>>a[i];
T.build(1,1,n);
while(q--)
{
int op,l,r;
cin>>op>>l>>r;
if(op==1)
{
int res[5]={};
T.query(1,1,n,l,r,res);
ll ans=0;
for(int i=0;i<5;i++)ans=(ans+(ll)c[i]*res[i])%mod;
cout<<ans<<'\n';
}
else if(op==2)
{
int x;
cin>>x;
int p[5];
for(int i=0;i<5;i++)p[i]=power(i,x);
T.update(1,1,n,l,r,p);
}
}
return 0;
}
```