版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ShadowGhostH/article/details/81605761
题目
题意
给定一个长度为n的序列,对序列定义两种操作:
在对应
的区间上,对
其中
为斐波那契数列的第
项。其中
对
区间元素求和,对
取模并输出。
分析
题意为一个较为经典的区间修改和区间查询问题,首先可以想到用线段树去维护,但是维护的难点在于,我们的区间修改并不是加上一个固定的值, 而是对应加上斐波那契数列的不同项。
我们考虑斐波那契数列的通项公式
其中,
则,
上式可以通过二次剩余和逆元等一些方法求得,查了几个二次剩余的博客没有看懂,自己直接写了个暴力求了一下。Orz
而我们得到了模意义下的斐波那契数列通项公式之后,我们可以发现,这个数列被拆分成了,两个等比数列的差乘以一个常数的形式。而根据等比数列的求和公式 ,我们可知对于一个区间的加操作,我们可以通过维护两个不同的首项来直接获得区间加了多少(对应区间的长度是已知的)而求和公式很容易可以看出,满足结合律,所以我们的lazy也就可以直接维护区间对应首项即可,维护首相的时候可以直接对lazy标记进行加和操作。
原问题就转化为了维护两个等比数列的区间求和,区间修改的线段树。
代码
#define int long long
/**
F(n)%mod = 276601605*(691504013^n-308495997^n)%mod mod=1e9+9
*/
const int q1 = 691504013;
const int q2 = 308495997;
const int mm = 276601605;
struct Node{
int l, r;
int sum1, sum2; //特征值
int lazy1, lazy2; //分别维护两个等比数列
}tree[maxn<<2]; //开四倍空间
int num[maxn]; //数值数组
int qn[2][maxn];
void init(){
ll now = 1;
qn[0][0] = now;
rep(i, 1, maxn){
now = now*q1%mod;
qn[0][i] = now;
}
qn[1][0] = now = 1;
rep(i, 1, maxn){
now = now*q2%mod;
qn[1][i] = now;
}
}
int qsum(int s, int flag, int n){
ll ans = s;
return mm*s%mod*(qn[flag][n]-1)%mod*qn[flag][1]%mod;
}
void build(int i, int l, int r){
tree[i].l = l;
tree[i].r = r;
if(l == r){
tree[i].sum1 = num[l];
tree[i].sum2 = 0;
tree[i].lazy1 = 0;
tree[i].lazy2 = 0;
return ;
}
int mid = (l+r)>>1;
build(i<<1, l, mid);
build(i<<1|1, mid+1, r); //子树建树后更新特征值
tree[i].sum1 = tree[i<<1].sum1 + tree[i<<1|1].sum1;
tree[i].sum2 = tree[i<<1].sum2 + tree[i<<1|1].sum2;
tree[i].lazy1 = 0;
tree[i].lazy2 = 0;
return ;
}
void push_down(int i){ //以lazy表示区间加为例更改特征值
if(tree[i].lazy1){
tree[i<<1].sum1 = (tree[i<<1].sum1 + qsum(tree[i].lazy1, 0, (tree[i<<1].r-tree[i<<1].l+1)))%mod;
tree[i<<1].sum2 = (tree[i<<1].sum2 + qsum(tree[i].lazy2, 1, (tree[i<<1].r-tree[i<<1].l+1)))%mod;
tree[i<<1|1].sum1 = (tree[i<<1|1].sum1 + qsum(tree[i].lazy1*qn[0][(tree[i<<1].r-tree[i<<1].l+1)]%mod, 0, (tree[i<<1|1].r-tree[i<<1|1].l+1)))%mod;
tree[i<<1|1].sum2 = (tree[i<<1|1].sum2 + qsum(tree[i].lazy2*qn[1][(tree[i<<1].r-tree[i<<1].l+1)]%mod, 1, (tree[i<<1|1].r-tree[i<<1|1].l+1)))%mod;
tree[i<<1].lazy1 = (tree[i<<1].lazy1+tree[i].lazy1)%mod;
tree[i<<1].lazy2 = (tree[i<<1].lazy2+tree[i].lazy2)%mod;
tree[i<<1|1].lazy1 = (tree[i<<1|1].lazy1+tree[i].lazy1*qn[0][(tree[i<<1].r-tree[i<<1].l+1)]%mod)%mod;
tree[i<<1|1].lazy2 = (tree[i<<1|1].lazy2+tree[i].lazy2*qn[1][(tree[i<<1].r-tree[i<<1].l+1)]%mod)%mod;
tree[i].lazy1 = tree[i].lazy2 = 0;
}
}
void addlr(int i, int l, int r, int s){ // num[l~r] += k
if(tree[i].l==l && tree[i].r==r){
tree[i].sum1 = (tree[i].sum1 + qsum(qn[0][s], 0, (tree[i].r-tree[i].l+1)))%mod;
tree[i].sum2 = (tree[i].sum2 + qsum(qn[1][s], 1, (tree[i].r-tree[i].l+1)))%mod;
//cout<<"*"<<l<<" "<<r<<" "<<qsum(qn[0][s], 0, (tree[i].r-tree[i].l+1))-qsum(qn[1][s], 1, (tree[i].r-tree[i].l+1))<<endl;
tree[i].lazy1 = (tree[i].lazy1 + qn[0][s])%mod;
tree[i].lazy2 = (tree[i].lazy2 + qn[1][s])%mod;
return ;
}
push_down(i);
int mid = (tree[i].l+tree[i].r)>>1;
if(mid >= r)
addlr(i<<1, l, r, s);
else if(mid < l)
addlr(i<<1|1, l, r, s);
else {
addlr(i<<1, l, mid, s);
addlr(i<<1|1, mid+1, r, s+(mid-l+1));
}
tree[i].sum1 = (tree[i<<1].sum1 + tree[i<<1|1].sum1)%mod;
tree[i].sum2 = (tree[i<<1].sum2 + tree[i<<1|1].sum2)%mod;
}
int query(int i, int l, int r){ //以sum为例
if(tree[i].l==l && tree[i].r==r)
return (tree[i].sum1-tree[i].sum2+mod)%mod;
push_down(i);
int mid = (tree[i].l+tree[i].r)>>1;
if(mid >= r)
return query(i<<1, l, r);
else if(mid < l)
return query(i<<1|1, l, r);
else
return (query(i<<1, l, mid) + query(i<<1|1, mid+1, r))%mod;
}
signed main()
{
init();
int n, m;
sldd(n, m);
rep(i, 1, n+1)
sld(num[i]);
build(1, 1, n);
rep(i, 0, m){
int x, l, r;
slddd(x, l, r);
if(x==1){
addlr(1, l, r, 1);
}
else if(x==2){
pld(query(1, l, r));
}
}
}
/*
4 4
1 2 3 4
1 1 4
2 1 4
1 2 4
2 1 3
*/
总结
这个线段树分别维护了两个
值和两个
标记,一开始写的时候写迷糊了,后来理清思路就好很多。
这次专门玩了一手define int long long 表示很开心,主函数改signed就可以了
代码因为开始思路不清晰所以有点丑
中间有多处取模可能会溢出或者忘记取模的部分,WA了两发,应多加注意