codeforces 446 c

题目的大意是你需要维护一个序列a,有两种操作:

1.对于区间[l,r],对于l<=i<=r,ai+=f[i-l+1],f是斐波那契数列

2.查询区间[l,r]的和

很久以前在培训的时候写过,然而并不会做,后来一直拖到退役也没有搞懂,今天心血来潮研究了一下,发现还是比较简单的。

很明显,这道题我们需要用线段树来维护,因为有区间操作,所以我们需要打标记,但是我们知道,线段树的标记应该满足可传递性,显然直接打标记是错误的

我们进行一个推倒:

设a,b是斐波那契数列中连续的两项,那么之后的序列应该是这样的:

a , b , a+b , a+2b , 2a+3b , 3a+5b...

我们发现从第三项开始a的系数是1,1,2,3,5

b的系数是1,2,3,5

也就是说其后的第i项应该是:a*f[i-2]+b*f[i-1]

那么我们对其后的n项求一个和,那么就应该是:a*(f[1]+f[2]+f[3]+...+f[n-2])+b*(f[1]+f[2]+f[3]+...+f[n-1])

那么我们发现想要维护一个区间[l,r]的标记我们只需要维护a和b即可,其后的取决于区间的长度

比如我们标记了[l,r]

那当我们下传标记的时候应该怎么做呢?

[l,(l+r)>>1] 的a和b直接下传即可

[(l+r)>>1|1,r]的a和b就要变化了,因为这里的(l+r)>>1|1实际上是[l,r]的第(l+r)>>1|1-l+1项,所以a就是(l+r)>>1|1-l+1,b是a+1

这样就可以做到下标的传递了,每次标记直接相加即可。

这道题也就解决了,代码过几天再写。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstdlib>
  4 #include<cstring>
  5 #include<string>
  6 #include<algorithm>
  7 #define mod 1000000009
  8 #define ll long long
  9 using namespace std;
 10 struct tree{
 11     int l,r;
 12     ll taga,tagb;
 13     ll sum;
 14 }f[1200005];
 15 int n,m;
 16 ll fib[300005],a[300005],sf[300005];
 17 void update(int i,int ta,int tb){
 18     int len=f[i].r-f[i].l+1;
 19     //cout<<f[i].sum<<endl;
 20     if(len>=2){
 21         ll tmp1=(ta*sf[len-2])%mod,tmp2=(tb*sf[len-1])%mod;
 22         tmp1=(tmp1+tmp2)%mod;
 23         //cout<<tmp1<<endl;
 24         f[i].sum=(f[i].sum+tmp1)%mod;
 25         f[i].sum=(f[i].sum+ta)%mod;
 26     }
 27     else{
 28         f[i].sum=(f[i].sum+ta)%mod;
 29         f[i].sum=(f[i].sum+tb*(len-1))%mod;
 30     }
 31     //cout<<f[i].sum<<endl;
 32     f[i].taga=(f[i].taga+ta)%mod;
 33     f[i].tagb=(f[i].tagb+tb)%mod;
 34     return ;
 35 }
 36 void pushup(int i){
 37     f[i].sum=(f[i<<1].sum+f[i<<1|1].sum)%mod;
 38     return ;
 39 }
 40 void pushdown(int i){
 41     if(f[i].taga){
 42         update(i<<1,f[i].taga,f[i].tagb);
 43         int mid=(f[i].r+f[i].l)>>1,len=(mid-f[i].l+1);
 44         //if(len>=2){
 45             ll tmp1=((f[i].taga*fib[len-1])%mod+(f[i].tagb*fib[len])%mod)%mod;
 46             ll tmp2=((f[i].taga*fib[len])%mod+(f[i].tagb*fib[len+1])%mod)%mod;
 47             update(i<<1|1,tmp1%mod,tmp2%mod);
 48        // }
 49         //else update(i<<1|1,f[i].tagb,(f[i].taga+f[i].tagb)%mod);
 50         f[i].taga=0;f[i].tagb=0;
 51         return ;
 52     }
 53     else return ;
 54 }
 55 void build(int i,int left,int right){
 56     f[i].l=left;f[i].r=right;
 57     if(left==right){
 58         f[i].sum=a[left];
 59         return ;
 60     }
 61     int mid=(left+right)>>1;
 62     build(i<<1,left,mid);build(i<<1|1,mid+1,right);
 63     pushup(i);
 64     //cout<<f[i].sum<<f[i<<1].sum<<f[i<<1|1].sum<<endl;
 65     return ;
 66 }
 67 void add(int i,int left,int right,int num){
 68     if(f[i].l==left&&f[i].r==right){
 69         update(i,fib[num],fib[num+1]);
 70         return ;
 71     }
 72     pushdown(i);
 73     int mid=f[i<<1].r,len=(mid-left+1);
 74     if(right<=mid)add(i<<1,left,right,num);
 75     else if(left>mid)add(i<<1|1,left,right,num);
 76     else add(i<<1,left,mid,num),add(i<<1|1,mid+1,right,num+len);
 77     pushup(i);
 78 }
 79 ll query(int i,int left,int right){
 80     if(f[i].l==left&&f[i].r==right)return f[i].sum;
 81     pushdown(i);
 82     int mid=(f[i].l+f[i].r)>>1;
 83     if(right<=mid)return query(i<<1,left,right);
 84     else if(mid<left)return query(i<<1|1,left,right);
 85     else return (query(i<<1,left,mid)+query(i<<1|1,mid+1,right))%mod;
 86 }
 87 int main(){
 88     //freopen("input.txt","r",stdin);
 89     fib[1]=1;fib[2]=1;sf[1]=1;sf[2]=2;
 90     ios_base::sync_with_stdio(false);
 91     cin.tie(0);
 92     cin>>n>>m;
 93     for(int i=1;i<=n;i++)
 94         cin>>a[i];
 95     for(int i=3;i<=n+3;i++){
 96         fib[i]=(fib[i-1]+fib[i-2])%mod;
 97         sf[i]=(sf[i-1]+fib[i])%mod;
 98     }
 99     build(1,1,n);
100     for(int i=1;i<=m;i++){
101         int x,y,z;
102         cin>>x>>y>>z;
103          //cout<<"SSS"<<endl;
104         if(x==1)add(1,y,z,1);
105         else cout<<query(1,y,z)<<'\n';
106          //cout<<"SSS"<<endl;
107     }
108     return 0;
109 }
View Code

 

转载于:https://www.cnblogs.com/htwx/articles/7624561.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值