• [洛谷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( egin{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} ight)]

    那么转移矩阵就可以直接通过答案矩阵来算了,如果不懂的话可以思考一下矩阵乘法的方法.

    下面是转移矩阵:

    [left( egin{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} ight)]

    然后矩阵乘法的部分就套一下板子就可以啦.

    还有一点,中间结果可能会爆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
    Ciocio 1
    Nicole 1" << endl, exit(0);
      if(n == 2) cout << "nodgd 3
    Ciocio 3
    Nicole 3" << endl, exit(0);
      init(), quick_pow(n-2);
      printf("nodgd %lld
    Ciocio %lld
    Nicole %lld
    ", ans.a[1][2], ans.a[1][4], ans.a[1][6]);
      return 0;
    }
    
  • 相关阅读:
    SQL Server 数据库部分常用语句小结(三)
    SQL Server 数据库部分常用语句小结(四)
    通过存储过程(SP)实现SQL Server链接服务器(LinkServer)的添加
    pcb布线强弱电间隔距离
    程序占用内存大小
    Offer来了(原理篇)笔记之第三章并发编程
    Offer来了(原理篇)笔记之第一章JVM原理
    西瓜视频奇妙的bug
    mongodb忘记了admin的账号密码
    MongoDB更改默认端口
  • 原文地址:https://www.cnblogs.com/BCOI/p/9889066.html
Copyright © 2020-2023  润新知