• 【9018题解】2109 卡德加的兔子


    题目描述

    卡德加有N个兔子笼,编号从1~N。刚开始每个兔子笼里有1对小兔,每个月小兔会长成大兔,之后的每个月这对大兔都会生出1对小兔。即兔子的繁殖遵循斐波那契数列的规律。
    例如:第一个月1对小兔,第二个月1对大兔,第三个月1对小兔1对大兔,第四个月1对小兔2对大兔,第五个月2对小兔3对大兔……
    卡德加学会了时间膜法,他可以对任意的[L,R]区间施加时间流逝K个月的膜法(如果他想要的话),使得这之中的兔子变为K个月后的景象。
    卡德加有时想喂兔子,所以他需要知道任意的[L,R]区间内的兔子数量,即求出[L,R]区间内共有多少兔子,大兔和小兔都需要算上。
    这些操作都是在一个月内完成的,所以不必考虑自然时间流逝对兔子的影响。

    输入

    第一行两个正整数N,M,表示兔子笼的数目和操作的数目。
    接下来M行,每行三个整数L,R,K。如果K>0,表示卡德加想对区间[L,R]施放时间流逝K个月的膜法。如果K=0,表示卡德加想要喂兔子了,输出区间[L,R]内兔子总共有多少对。

    输出

    对每个喂兔子的操作,输出一行,一个数,表示兔子的对数。答案对10007取模。

    样例输入

    10 10
    1 3 2
    1 1 0
    2 4 0
    3 5 0
    4 7 3
    3 5 0
    1 4 0
    2 7 0
    1 9 4
    2 10 0

    样例输出

    2
    5
    4
    8
    9
    16
    121

    题解

    很显然的线段树类型题,但是操作不简单。
    考虑区间[L,R]内有A只小兔,B只大兔,那么经过K个月后,会有多少只呢?
    根据斐波那契数列的递推式,写出矩阵,再写出公式:
    (egin{bmatrix}0&1\1&1end{bmatrix}^{K}cdotegin{bmatrix}A\Bend{bmatrix}=egin{bmatrix}A'\B'end{bmatrix})
    A'和B'就是K月之后的小兔和大兔数量。
    对于矩阵乘方,可以使用矩阵快速幂。之后就可以和普通的线段树一样操作。
    需要注意的是,这题有点卡常,所以注意对矩阵乘法和其它细节上的常数优化。
    优化①:对于2*2的小矩阵,不写结构体,不用循环,直接乘,会加快速度。
    优化②:这是一个很有技巧的优化,注意到矩阵(egin{bmatrix}0&1\1&1end{bmatrix}^{20016})在模10007意义下等于单位矩阵,即K=K%20016对结果没有影响。根据这一点可以进行优化。这一点是如何想到的呢?首先应该想到,在模意义下,一切递推式均会重复。然后可以打表找规律,找循环节。对于斐波那契数列,有定理宣称,在模意义下,循环节长度不会超过模数的6倍。
    优化③:继续在②的基础上优化,预处理出矩阵的0~20015次幂,做乘法时直接用,直接消灭了log和矩阵乘法的常数。
    代码如下:

     1 #include<cstdio>
     2 #include<cstring>
     3 #define F(i,a,b) for(int i=a;i<=b;++i)
     4 #define Mod 10007//题目要求的模数
     5 #define Mod2 20016//循环节长度
     6 #define getchar() (S==TT&&(TT=(S=BB)+fread(BB,1,1<<15,stdin),TT==S)?EOF:*S++)
     7 char BB[1<<15],*S=BB,*TT=BB;//快读而已
     8 inline int I(){//快读
     9     int x=0;char c=getchar();
    10     while(c<'0'||c>'9')c=getchar();
    11     while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+c-'0',c=getchar();
    12     return x;
    13 }
    14 int a1[262145],a2[262145],lazy[262145];
    15 int A00[20016],A01[20016],A10[20016],A11[20016];//处理前20016次幂
    16 int n,m,a,b,k,tmpa1,tmpa2;
    17 inline int mo(int x){return x<Mod2?x:x-Mod2;}
    18 void init(){//预处理过程
    19     A00[0]=A11[0]=1, A01[0]=A10[0]=0;
    20     F(i,1,20015) A00[i]=A01[i-1], A01[i]=(A00[i-1]+A01[i-1])%Mod, A10[i]=A11[i-1], A11[i]=(A10[i-1]+A11[i-1])%Mod;
    21 }
    22 void Mogic(int&x,int&y,int K){//对[x,y]施放时间流逝K月
    23     tmpa1=(A00[K]*x+A01[K]*y)%Mod, tmpa2=(A10[K]*x+A11[K]*y)%Mod;
    24     x=tmpa1, y=tmpa2;
    25 }
    26 inline void push_down(int l,int r,int i){
    27     if(!lazy[i]) return;
    28     Mogic(a1[i<<1],a2[i<<1],lazy[i]);
    29     Mogic(a1[i<<1|1],a2[i<<1|1],lazy[i]);
    30     lazy[i<<1]=mo(lazy[i<<1]+lazy[i]);
    31     lazy[i<<1|1]=mo(lazy[i<<1|1]+lazy[i]);
    32     lazy[i]=0;
    33 }
    34 void build(int l,int r,int i){ a1[i]=(r-l+1)%Mod; if(l<r) build(l,(l+r)>>1,i<<1), build(((l+r)>>1)+1,r,i<<1|1);}
    35 void Xu(int l,int r,int i){//时间流逝
    36     if(r<a||b<l) return;
    37     if(a<=l&&r<=b) { Mogic(a1[i],a2[i],k); lazy[i]=mo(lazy[i]+k); return; }
    38     push_down(l,r,i);
    39     Xu(l,(l+r)>>1,i<<1);
    40     Xu(((l+r)>>1)+1,r,i<<1|1);
    41     a1[i]=(a1[i<<1]+a1[i<<1|1])%Mod;
    42     a2[i]=(a2[i<<1]+a2[i<<1|1])%Mod;
    43 }
    44 int Feed(int l,int r,int i){//喂兔子
    45     if(r<a||b<l) return 0;
    46     if(a<=l&&r<=b) return (a1[i]+a2[i])%Mod;
    47     push_down(l,r,i);
    48     return (Feed(l,(l+r)>>1,i<<1)+Feed(((l+r)>>1)+1,r,i<<1|1))%Mod;
    49 }
    50 int main(){
    51     init();
    52     n=I(),m=I();
    53     build(1,n,1);//建树
    54     F(i,1,m){
    55         a=I(),b=I(),k=I();
    56         if(k) { if(k%=Mod2) Xu(1,n,1); }//这里如果k是20016的倍数,就不用动了
    57         else printf("%d
    ",Feed(1,n,1));
    58     }
    59     return 0;
    60 }
  • 相关阅读:
    mysql为什么使用b+树 不使用b树 或 红黑树
    面试篇 CSS
    练习 : Flink 批流一体
    BiliNote使用教程
    (转) DNS各地延迟排行榜(主)
    练习 :Flink 字频统计
    JSON常用方法
    练习 : 自定义sink mysql hbase
    maxWell
    练习:flink table
  • 原文地址:https://www.cnblogs.com/PinkRabbit/p/7429964.html
Copyright © 2020-2023  润新知