codeforces316E3 Summer Homework(線段樹,斐波那契數列)
題目大意
給定一個n個數的數列,m個操作,有三種操作:
\(1\ x\ v\) 將\(a_x\)的值修改成v
\(2\ l\ r\\) 求 \(\sum_{i=l}^r x_i*f_{i-l}\) 其中對於\(f\)數組 \(f_0=1\ ,f_1=1\ ,f_i=f_{i-1}+f_{i-2}\) (就是斐波那契數列)
\(3\ l\ r\ x\\) 讓\(a_i+x,i\in[l,r]\)
其中\(n\le 100000,m\le 100000\)
一看這個題QwQ,就知道是線段樹題
QwQ那麽怎麽維護節點信息和合並區間呢
來舉個栗子試一下
對於系數分別為\(1\ 1,1\ 2\)來說
將二者相加變為\(2\ 3\)
由於fib序列相加還是fib序列
哇,那這麽說,這個題目所給的求和的操作,也是可以通過已知矩陣乘轉移矩陣快速得到目標矩陣
那麽!就可以通過這個東西來轉移了!
\[
\begin{bmatrix}
0 & 1 \1& 1 \\end{bmatrix}
\]
所以!對於左右區間來說,合並的時候,只需要把右區間乘上左區間的長度次方(就相當於把右邊這個區間的變為\(f_{mid-l+1}\)項開頭)
同時,我們發現要進行矩陣轉移,必須記錄當前這個區間的元素從\(f_0\)開始和\(f_1\)開始的兩個值,才能夠進行矩陣計算
QwQ因為我不會矩陣乘法呀!
所以我是選擇手動展開了矩陣的n次方
最後假設是求矩陣的n次方的話
那麽最終的矩陣應為
\[
\begin{bmatrix}
{fib}_{n-2} & {fib}_{n-1} \{fib}_{n-1}& {fib}_{n} \\end{bmatrix}
\]
~只需要預處理一下fib序列和fib序列的前綴和就行了
void up(int root) { ll len = f[2*root].len; f[root].len=(f[2*root].len+f[2*root+1].len)%mod; f[root].fir=(f[2*root].fir+f[2*root+1].fir*get(len-2)+f[2*root+1].sec*fib[len-1])%mod; f[root].sec=(f[2*root].sec+f[2*root+1].fir*fib[len-1]+f[2*root+1].sec*fib[len])%mod; }
接著,我們考慮,對3操作
如果讓一個區間加x,就是讓這個區間加x*fib前綴和的區間長度-1項(因為\(f_0=1\))(求答案的是從\(f_0\)開始乘)
emmmm所以也是可以直接做了咯(記得從1開始乘的那個信息需要-add[root])
void pushdown(int root,int l,int r)
{
if (add[root])
{
add[2*root]=(add[2*root]+add[root])%mod;
add[2*root+1]=(add[2*root+1]+add[root])%mod;
f[2*root].fir=(f[2*root].fir+add[root]*sum[f[2*root].len-1])%mod;
f[2*root].sec=(f[2*root].sec+add[root]*sum[f[2*root].len]-add[root])%mod;
f[2*root+1].fir=(f[2*root+1].fir+add[root]*sum[f[2*root+1].len-1])%mod;
f[2*root+1].sec=(f[2*root+1].sec+add[root]*sum[f[2*root+1].len]-add[root])%mod; //之所以-1是因為要減掉fib[0]
add[root]=0;
}
}
update和change和build都差不多~
需要註意的是!!!!!!!!!
query的時候,不能直接\(return\ f[root].fir\)
因為如果讓區間為\([l,r]\),就需要將這一段嫁接到\([x,l-1]\)的後面,對,所以也需要想之前合並的時候那樣乘一個fib
if (x<=l && r<=y)
{
int len = l-1-x+1;
if (len==0) return f[root].fir;
return f[root].fir*get(len-2)+f[root].sec*get(len-1);
}
其他的都差不多了啦
直接上代碼!
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<map>
#include<vector>
#define ll long long
using namespace std;
inline ll read()
{
ll x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch==‘-‘) f=-1;ch=getchar();}
while (isdigit(ch)) {x=x*10+ch-‘0‘;ch=getchar();}
return x*f;
}
const int maxn = 2e5+1e2;
const int mod = 1000000000;
struct Node
{
ll fir,sec,len;
};
Node f[4*maxn];
ll add[4*maxn];
ll a[maxn];
ll fib[maxn],sum[maxn];
int n,m;
void init()
{
fib[0]=1,fib[1]=1;
for (int i=2;i<=n;i++) fib[i]=(fib[i-1]+fib[i-2])%mod;
sum[0]=1;
for (int i=1;i<=n;i++) sum[i]=(sum[i-1]+fib[i])%mod;
}
ll get(int x)
{
if (x<0) return 0;
else return fib[x];
}
void up(int root)
{
ll len = f[2*root].len;
f[root].len=(f[2*root].len+f[2*root+1].len)%mod;
f[root].fir=(f[2*root].fir+f[2*root+1].fir*get(len-2)+f[2*root+1].sec*fib[len-1])%mod;
f[root].sec=(f[2*root].sec+f[2*root+1].fir*fib[len-1]+f[2*root+1].sec*fib[len])%mod;
}
void pushdown(int root,int l,int r)
{
if (add[root])
{
add[2*root]=(add[2*root]+add[root])%mod;
add[2*root+1]=(add[2*root+1]+add[root])%mod;
f[2*root].fir=(f[2*root].fir+add[root]*sum[f[2*root].len-1])%mod;
f[2*root].sec=(f[2*root].sec+add[root]*sum[f[2*root].len]-add[root])%mod;
f[2*root+1].fir=(f[2*root+1].fir+add[root]*sum[f[2*root+1].len-1])%mod;
f[2*root+1].sec=(f[2*root+1].sec+add[root]*sum[f[2*root+1].len]-add[root])%mod; //之所以-1是因為要減掉fib[0]
add[root]=0;
}
}
void build(int root,int l,int r)
{
if (l==r)
{
f[root].sec=f[root].fir=a[l]%mod;
f[root].len=1;
return;
}
int mid = (l+r) >> 1;
build(2*root,l,mid);
build(2*root+1,mid+1,r);
up(root);
}
void update(int root,int l,int r,int x,int y,int p)
{
if (x<=l && r<=y)
{
add[root]=(add[root]+p)%mod;
f[root].fir=(f[root].fir+sum[r-l]*p)%mod;
f[root].sec=(f[root].sec+sum[r-l+1]*p-p)%mod;
return;
}
pushdown(root,l,r);
int mid = (l+r) >> 1;
if (x<=mid) update(2*root,l,mid,x,y,p);
if (y>mid) update(2*root+1,mid+1,r,x,y,p);
up(root);
}
void change(int root,int l,int r,int x,int p)
{
if (l==r)
{
f[root].fir=f[root].sec=p%mod;
add[root]=0;
f[root].len=1;
return;
}
pushdown(root,l,r);
int mid = (l+r) >> 1;
if (x<=mid) change(2*root,l,mid,x,p);
if (x>mid) change(2*root+1,mid+1,r,x,p);
up(root);
}
ll query(int root,int l,int r,int x,int y)
{
if (x<=l && r<=y)
{
int len = l-1-x+1;
if (len==0) return f[root].fir;
return f[root].fir*get(len-2)+f[root].sec*get(len-1);
}
int mid = (l+r) >> 1;
pushdown(root,l,r);
ll ans=0;
if (x<=mid) ans=(ans+query(2*root,l,mid,x,y))%mod;
if (y>mid) ans=(ans+query(2*root+1,mid+1,r,x,y))%mod;
return ans%mod;
}
int main()
{
scanf("%d%d",&n,&m);
init();
for (int i=1;i<=n;i++) a[i]=read();
build(1,1,n);
//cout<<query(1,1,n,1,4)<<endl;
for (int i=1;i<=m;i++)
{
int opt;
opt=read();
if (opt==1)
{
int x=read();
ll y=read();
change(1,1,n,x,y);
}
if (opt==2)
{
int x=read(),y=read();
//cout<<x<<" "<<y<<endl;
printf("%lld\n",query(1,1,n,x,y));
//cout<<query(1,1,n,1,4)<<endl;
}
if (opt==3)
{
int x=read(),y=read();
ll z=read();
update(1,1,n,x,y,z);
}
}
return 0;
}
codeforces316E3 Summer Homework(線段樹,斐波那契數列)