1. 程式人生 > >[SHOI2015]超能粒子炮·改

[SHOI2015]超能粒子炮·改

str 遞推 msu 數據 一行 lap 整數 return sin

題目描述

曾經發明了腦洞治療儀與超能粒子炮的發明家 SHTSC 又公開了他的新發明:超能粒子炮?改——一種可以發射威力更加強大的粒子流的神秘裝置。

超能粒子炮?改相比超能粒子炮,在威力上有了本質的提升。它有兩個參數 n , k ,它會向每個編號為 0k (包含兩端)的位置 i發射威力為Cni ?mod2333 的粒子流。

現在 SHTSC 給出了他的超能粒子炮?改的參數,讓你求出其發射的粒子流的威力之和除以 2333 所得的余數。

輸入輸出格式

輸入格式:

第一行一個整數 t表示數據組數。 之後 t行,每行兩個整數 nk ,含義如題面描述。

輸出格式:

t行,每行一個整數,表示其粒子流的威力之和模 2333 的值。

思路:

我們先看一下樣例(p=5,k=10,m=13)

13 0 1 2 3 4 5 6 7 8 9 10

1 3 0 1 2 3 4 0 1 2 3 4 0

5 2 0 0 0 0 0 1 1 1 1 1 2

我們可以驚奇的發現,這是可以用lucas合並的

為什麽呢?

這麽多重復的(00000)(11111)

明顯可以合並加速

那怎麽合並呢?
我們可以通過預處理組合數的辦法(p只有2333),提前求出組合數c

再求出組合數前綴和S

然後遞推公式是這個:

S(n,k)mod p=[S(n/p,k/p-1)*S(n mod p,p-1)+C(n/p,k/p)*S(n mod p,k mod p)]mod p

再套回去,就出來了

Code:

#include<iostream>
#include<cstdio>
#define rii register int i
#define rij register int j
#define rit register int t
#define ll long long
using namespace std;
long long i,j,k,m,n,x,y,z,p=2333,q;
long long c[2335][2335],s[2335][2335];
int C(ll x,ll y)
{
    return (x<p)?c[x][y]:C(x/p,y/p)*c[x%p][y%p]%p;
}
int S(ll n,ll k)
{
    return (n<p)?s[n][k]:(s[n%p][p-1]*S(n/p,k/p-1)+C(n/p,k/p)*s[n%p][k%p])%p;
}
int main()
{    
    c[0][0]=s[0][0]=1;
    for(rii=1;i<=p;i++)
    {
        s[0][i]=1;
    }
    for(rii=1;i<=p;i++)
    {
        for(rij=0;j<=i;j++)
        {
        	c[i][j]=(c[i-1][j]+c[i-1][j-1])%p;
            s[i][j]=(s[i][j-1]+c[i][j])%p;
        }
        for(rij=i+1;j<=p;j++)
        {
            s[i][j]=s[i][j-1];
        }
    }
    cin>>q;
    for(rit=1;t<=q;t++)
    {
        scanf("%ld",&n);
        scanf("%ld",&k);
        printf("%ld\n",S(n,k));
    }
}

  

[SHOI2015]超能粒子炮·改