[洛谷P1707] 刷題比賽
洛谷題目連線:刷題比賽
題目背景
nodgd是一個喜歡寫程式的同學,前不久洛谷OJ橫空出世,nodgd同學當然第一時間來到洛谷OJ刷題。於是發生了一系列有趣的事情,他就打算用這些事情來出題噁心大家……
題目描述
洛谷OJ當然算是好地方,nodgd同學打算和朋友分享一下。於是他就拉上了他的朋友Ciocio和Nicole兩位同學一起刷題。喜歡比賽的他們當然不放過這樣一次刷題比賽的機會!
在第1天nodgd,Ciocio,Nicole都只做了1道題。
在第2天nodgd,Ciocio,Nicole都只做了3道題。
他們都有著嚴格的刷題規則,並且會在每一天都很遵守規則的刷一定量的題。
(1)nodgd同學第k+2天刷題數量a[k+2]=p * a[k+1]+q * a[k]+b[k+1]+c[k+1]+r * k^2+t * k+1;
(2)Ciocio同學第k+2天刷題數量b[k+2]=u * b[k+1]+v * b[k]+a[k+1]+c[k+1]+w^k;
(3)Nicole同學第k+2天刷題數量c[k+2]=x * c[k+1]+y * c[k]+a[k+1]+b[k+1]+z^k+k+2;
(以上的字母p,q,r,t,u,v,w,x,y,z都是給定的常數,並保證是正整數)
於是他們開始了長時間的刷題比賽!一共進行了N天(4<=N<=10^16)
但是時間是可貴的,nodgd想快速知道第N天每個人的刷題數量。不過nodgd同學還有大量的數學競賽題、物理競賽題、英語競賽題、美術競賽題、體育競賽題……要做,就拜託你來幫他算算了。
由於結果很大,輸出結果mod K的值即可。
輸入輸出格式
輸入格式:
第一行兩個正整數N,K。(4<=N<=10^ 16,2<=K<=10^16)
第二行四個正整數p,q,r,t。
第三行三個正整數u,v,w。
第四行三個正整數x,y,z。
(保證p,q,r,t,u,v,w,x,y,z都是不超過100的正整數)
輸出格式:
共三行,每行一個名字+一個空格+一個整數。依次是nodgd,Ciocio,Nicole和他們在第N天刷題數量mod K的值。
輸入輸出樣例
輸入樣例#1:
4 10007
2 1 1 1
2 2 3
1 1 2
輸出樣例#1:
nodgd 74
Ciocio 80
Nicole 59
說明
矩陣乘法。
注意,中間相乘過程可能會比64位長整型的資料範圍還要大。
題意: 給你三個遞推式,要你遞推出三個遞推式的第\(n\)項.
題解: 今天考試考這題結果考試的時候腦子抽了,沒調出來.
首先這題線性遞推,是一個矩陣轉移是肯定沒問題的.
那麼我們考慮如何構造這個矩陣.
當時我想的是將所有轉移需要用到的量都放到矩陣中轉移(就是常量和那些變數),然後構出來一個矩陣有30*30多的大小,填矩陣填到心態炸裂.
但其實是沒必要填這麼多元素進去的.
一般構造矩陣的方法就是將所有變數存入答案矩陣中.
但是隻有這樣還不夠,為了遞推答案矩陣,我們還需要將遞推答案矩陣所需要的變數都放進矩陣中轉移.
我們發現轉移中有\(r*i^2, w ^i,z ^i\),那麼我們在轉移的同時也要轉移這些變數.
為了能轉移\(i^2\),同時我們需要將\(i,1\)也放進矩陣中.因為\((i+1)^2=i ^2+2*i+1\)
既然這樣,我們可以構造答案矩陣:
\[\left( \begin{matrix} a_{i-2},a_{i-1},b_{i-2},b_{i-1},c_{i-2},c_{i-1},(i-2)^2,(i-2),1,w^{i-2},z^{i-2} \end{matrix} \right)\]
那麼轉移矩陣就可以直接通過答案矩陣來算了,如果不懂的話可以思考一下矩陣乘法的方法.
下面是轉移矩陣:
\[\left( \begin{matrix} 0 & q & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 1 & p & 0 & 1 & 0 & 1 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & v & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 1 & 1 & u & 0 & 1 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & y & 0 & 0 & 0 & 0 & 0 \\ 0 & 1 & 0 & 1 & 1 & x & 0 & 0 & 0 & 0 & 0 \\ 0 & r & 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 \\ 0 & t & 0 & 0 & 0 & 1 & 2 & 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 & 0 & 2 & 1 & 1 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & w & 0 \\ 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 & z \\ \end{matrix} \right)\]
然後矩陣乘法的部分就套一下板子就可以啦.
還有一點,中間結果可能會爆long long.
那麼對於兩個long long相乘再mod int可以用慢速乘來實現.
其實慢速乘的原理和快速冪是一樣的,也就是將一次乘法轉化乘log次加法,因為加法不會爆long long,所以能夠保證最終結果不會爆long long.
#include<bits/stdc++.h>
using namespace std;
typedef int _int;
#define int long long
const int N = 100000+5;
int n, yyj, p, q, r, t, u, v, w, x, y, z;
int a[N], b[N], c[N];
int mul(int x, int y){
int res = 0;
for(; y; y >>= 1, (x += x) %= yyj)
if(y & 1) res += x;
return res;
}
struct matrix{
int a[25][25];
matrix operator * (matrix x){
matrix res; memset(res.a, 0, sizeof(res.a));
for(int i = 1; i <= 11; i++)
for(int j = 1; j <= 11; j++)
for(int k = 1; k <= 11; k++)
(res.a[i][j] += mul(a[i][k], x.a[k][j])) %= yyj;
return res;
}
}ans, tmp;
void init(){
memset(tmp.a, 0, sizeof(tmp.a));
memset(ans.a, 0, sizeof(ans.a));
tmp = (matrix){
{
{}, // 0
{ 0, 0, q, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 1
{ 0, 1, p, 0, 1, 0, 1, 0, 0, 0, 0, 0 }, // 2
{ 0, 0, 0, 0, v, 0, 0, 0, 0, 0, 0, 0 }, // 3
{ 0, 0, 1, 1, u, 0, 1, 0, 0, 0, 0, 0 }, // 4
{ 0, 0, 0, 0, 0, 0, y, 0, 0, 0, 0, 0 }, // 5
{ 0, 0, 1, 0, 1, 1, x, 0, 0, 0, 0, 0 }, // 6
{ 0, 0, r, 0, 0, 0, 0, 1, 0, 0, 0, 0 }, // 7
{ 0, 0, t, 0, 0, 0, 1, 2, 1, 0, 0, 0 }, // 8
{ 0, 0, 1, 0, 0, 0, 2, 1, 1, 1, 0, 0 }, // 9
{ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, w, 0 }, // 10
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, z }, // 11
}
};
ans = (matrix){
{
{},
{ 0, 1, 3, 1, 3, 1, 3, 1, 1, 1, w, z },
}
};
}
void quick_pow(int n){
for(; n; n >>= 1, tmp = tmp*tmp)
if(n & 1) ans = ans*tmp;
}
_int main(){
freopen("shuati.in", "r", stdin);
freopen("shuati.out", "w", stdout);
cin >> n >> yyj >> p >> q >> r >> t >> u >> v >> w >> x >> y >> z;
if(n == 1) cout << "nodgd 1\nCiocio 1\nNicole 1" << endl, exit(0);
if(n == 2) cout << "nodgd 3\nCiocio 3\nNicole 3" << endl, exit(0);
init(), quick_pow(n-2);
printf("nodgd %lld\nCiocio %lld\nNicole %lld\n", ans.a[1][2], ans.a[1][4], ans.a[1][6]);
return 0;
}