1. 程式人生 > >[洛谷P1707] 刷題比賽

[洛谷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;
}