1. 程式人生 > >小紅帽的畫筆(NOIP模擬賽Round 7)

小紅帽的畫筆(NOIP模擬賽Round 7)

一個數 .com urn 只需要 1-1 前綴 每次 高精 mat

又到了神奇的模擬賽時間~

真是喪~

好吧我們來看看題目

小紅帽是Pop star上最著名的人類畫家,她可以將任何畫出的東西變成真實的物品。賦予她這樣神奇能力的正是她手上的畫筆。

小紅帽每次作畫時,都需要用到她的調色盤,我們把每個自然數都對應一種顏色,那麽小紅帽的調色盤就可以看成是一個斐波那契數列(數列第12項都為1,小紅帽每次需要一種顏色時,她都會用畫筆蘸取一段區間,得到的顏色就是區間裏所有的數之和。

受到秋之國人民的邀請,小紅帽要為他們畫一個夏天。小紅帽要進行n次取色,給出每次蘸取的區間[l,r],作為小C委派來進行記錄的你需要輸出每次小紅帽得到的顏色,答案對mod取模

【數據範圍】

對於10%的數據,n<=100,l,r<=10^4;

對於30%的數據,l,r<=10^7;

對於90%的數據,mod<=10^9;

對於100%的數據,0<=n<=1000,1<=l<=r<=10^18,0<mod<=10^18。

————————————————我是分割線————————————————————

很顯然,這道題目就是求斐波那契數列前r項的前綴和減去前l-1項的前綴和,普通的DP都可以做到求斐波那契數列,但是很顯然10^18我們就會T

在此我們講講矩陣乘法

矩陣乘法,顧名思義,就是2個矩陣相乘。具體如下

技術分享

所以呢我們如果要算斐波那契數列的第N項只需要將圖中的技術分享矩陣自乘n-2次,再乘第一個矩陣,得到的矩陣的第一個數就是答案

那麽我們又怎樣求前綴和呢?

在此有兩種方法供參考

TOP1:找規律

我們假設a,b為斐波那契數列的第一項和第二項

那麽我們很顯然就可以遞推出後面的幾項

技術分享

那麽這有什麽規律呢?

很快就發現了規律

a=a+b-b;a+b=a+2b-b;2a+2b=2a+3b-b;3a+4b=3a+5b-b.....

所以我們只需要求num[r+2]-1-(num[l-1+r]-1)=num[r+2]-num[l-1]即可啦

TOP2:構造矩陣

顯然我們知道我們要保留答案矩陣的前面2個數,而我們想辦法構造出第三個數,用於計算前綴和。這樣將這個矩陣自乘n-2次,輸出第三個數就好啦。

然後我們會想到我們的前綴和就是sum[i]=sum[i-1]+num[i];

然後就會構造出這個矩陣啦技術分享

———————————————我是分割線—————————————————

那麽我們還看到一個問題,如何處理mod?

我們知道如果mod為10^18

那麽一次乘法操作的數會達到10^36

如果是這樣我們就需要做高精除+高精乘了。

但是有沒有更快的方法?

首先我們知道如果在加法中進行取余,結果不改變。

所以我們將乘法轉變為加法

這樣速度雖然慢了點,卻不會爆long long

然後這道題就愉快解決啦!

下面貼代碼

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n;
struct matrix{
    unsigned long long mat[2][2];
};
unsigned long long l,r,mod;
long long mul(long long x,long long y)
{
    if (x<y) swap(x,y);
    register long long z=0;
    for (;y;y>>=1,x<<=1,x=x>=mod?x-mod:x) if (y&1) z+=x,z=z>=mod?z-mod:z;
    return z;
}
matrix multiply(matrix a,matrix b)
{
    unsigned long long sum=0;
    matrix c;
    memset(c.mat,0,sizeof(c.mat));
    for(int i=0;i<=1;i++)
        for(int j=0;j<=1;j++)
        {
            sum=0;
            for(int k=0;k<=1;k++)
            {
                long long numqq=mul(a.mat[i][k],b.mat[k][j]);
                if(mod>1000000000)sum+=numqq,sum%=mod;
                else sum+=(a.mat[i][k]*b.mat[k][j])%mod;
            }
            c.mat[i][j]=sum;
        }
    return c;    
} 
matrix matmod(matrix a,unsigned long long k)
{
    matrix res;
    memset(res.mat,0,sizeof(res.mat));
    for(int i=0;i<=1;i++)res.mat[i][i]=1;
    while(k)
    {
        if(k&1)res=multiply(res,a);
        k>>=1;
        a=multiply(a,a);
    }
    return res;
}
unsigned long long work(unsigned long long x)
{
    if(x==0)return 0;
    if(x==1)return 1;
    if(x==2)return 2;
    matrix a,b;
    memset(a.mat,0,sizeof(a.mat));
    memset(b.mat,0,sizeof(b.mat));
    for(int i=0;i<=1;i++)a.mat[i][1]=1;
    for(int i=0;i<=1;i++)b.mat[i][0]=1;
    a.mat[1][0]=1; 
    a=matmod(a,x);
    a=multiply(a,b);
    return (a.mat[1][0]-1+mod)%mod;
} 
int main(){
    freopen("artist.in","r",stdin);
    freopen("artist.out","w",stdout);
    scanf("%d%lld",&n,&mod);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld%lld",&l,&r);
        unsigned long long num1=0,num2=0;
        num1=work(l-1);
        num2=work(r);
        unsigned long long ans=num2-num1+mod;
        printf("%lld\n",ans%mod); 
    }
    return 0;
    fclose(stdin);
    fclose(stdout);
} 

小紅帽的畫筆(NOIP模擬賽Round 7)