洛谷P1707 刷题比赛

题目背景

nodgd是一个喜欢写程序的同学,前不久洛谷OJ横空出世,nodgd同学当然第一时间来到洛谷OJ刷题。于是发生了一系列有趣的事情,他就打算用这些事情来出题恶心大家……

题目描述

洛谷OJ当然算是好地方,nodgd同学打算和朋友分享一下。于是他就拉上了他的朋友Ciocio和Nicole两位同学一起刷题。喜欢比赛的他们当然不放过这样一次刷题比赛的机会!

在第1天nodgd,Coicoi,Nicole都只做了1道题。

在第2天nodgd,Coicoi,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位长整型的数据范围还要大。

矩阵快速幂。。。

矩阵如下(造了我10min+):

【 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 u 0 0 0 0 0 0 0 】

【 0 1 1 v 0 1 0 0 0 0 0 】

【 0 0 0 0 0 x 0 0 0 0 0 】

【 0 1 0 1 1 y 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 0 0 1 0 0 0 0 w 0 0 】

【 0 0 0 0 0 1 0 0 0 z 0 】

【 0 1 0 0 0 2 1 1 0 0 1 】

剩下的基本是板子。。。

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define MAXN 15
using namespace std;
unsigned long long n,mod;
struct node{
    unsigned long long a[MAXN][MAXN];
}base,ans;
inline long long read(){
	long long date=0,w=1;char c=0;
	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
	return date*w;
}
unsigned long long mul(unsigned long long x,unsigned long long y){
    unsigned long long s=0;
    while(y){
        if(y&1)s=(s+x)%mod;
        x=(x<<1)%mod;
        y>>=1;
    }
    return s%mod;
}
node operator *(const node &x,const node &y){
    node ret;
    for(int i=1;i<=11;i++)
    for(int j=1;j<=11;j++){
        ret.a[i][j]=0;
        for(int k=1;k<=11;k++){
            ret.a[i][j]+=mul((x.a[i][k]%mod),(y.a[k][j]%mod))%mod;
            ret.a[i][j]%=mod;
        }
    }
    return ret;
}
void mexp(long long k){
    while(k){
        if(k&1)ans=ans*base;
        base=base*base;
        k>>=1;
    }
}
int main(){
    n=read()-2;mod=read();
    memset(base.a,0,sizeof(base.a));
    base.a[1][1]=base.a[1][2]=base.a[1][3]=base.a[1][9]=base.a[2][2]=base.a[2][11]=base.a[3][3]=base.a[4][11]=base.a[5][10]=1;
    base.a[9][6]=base.a[9][10]=base.a[9][11]=base.a[10][7]=base.a[10][9]=base.a[10][11]=base.a[11][8]=base.a[11][9]=base.a[11][10]=1;
    base.a[1][11]=base.a[2][3]=2;
    base.a[9][9]=read();base.a[6][9]=read();base.a[3][9]=read();base.a[2][9]=read();
    base.a[10][10]=read();base.a[7][10]=read();base.a[5][5]=read();
    base.a[11][11]=read();base.a[8][11]=read();base.a[4][4]=read();
    ans.a[1][1]=ans.a[1][2]=ans.a[1][3]=ans.a[1][6]=ans.a[1][7]=ans.a[1][8]=1;
    ans.a[1][4]=base.a[4][4];ans.a[1][5]=base.a[5][5];ans.a[1][9]=ans.a[1][10]=ans.a[1][11]=3;
    mexp(n);
    printf("nodgd %lld\nCiocio %lld\nNicole %lld\n",ans.a[1][9]%mod,ans.a[1][10]%mod,ans.a[1][11]%mod);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值