【NOIP2016提高A组模拟7.21】Clock Sequence

37 篇文章 0 订阅
9 篇文章 0 订阅

Description

科学家温斯顿定义了一个无限重复的数列:1234321234321234321……,并将其称为时钟序列。
他发现可以将数列人为分成几段:
1, 2, 3, 4, 32, 123, 43, 2123, 432, 1234, 32123, …
他又定义了新数列中第n项为Vn,这样分组能够满足Vn的数字和等于n。例如,V2=2,V7=43,V11=32123。
请帮助他求出数列V的前n项和。

Input

第一行一个正整数,表示n。

Output

第一行一个整数,表示数列V前n项和对123454321取模后的值。

Sample Input

输入1:
11
输入2:
1000

Sample Output

输出1:
36120
输出2:
18232686

Data Constraint

Subtask1[20pts]:N<=40
Subtask2[20pts]:N<=1000
Subtask3[60pts]:N<=10^14

Solution

这题奇奇怪怪
40分是显然的,直接 n2 暴力就可以得到
然后你需要打个表,就可以发现,数字是每15个一组的
比如说
1->1
2->2
3->3
4->4
5->32

15->1 23432
16->1 234321

30->123432 123432
31->1 234321 234321

46->1 234321 234321 234321

发现了什么?
每个数都是AB…的形式的
比如说每组的第一个数,就是1、16、31、46等
A=1,B=234321
16时是AB
31时是ABB
46时是ABBB
发现这个规律后,就可以打一个15的表,后面的数用矩阵乘法求出
当然,也可以用数学方法如等比数列的方式,但是由于mo数不是质数,要用exgcd,懒得打,而且矩阵乘法方便很多

Code

#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define mo 123454321
#define ll long long
#define M 1000000
using namespace std;
ll A[16]={0,1,2,3,4,32,123,43,2123,432,1234,32123,43212,34321,23432,123432};
ll B[16]={0,234321,343212,432123,321234,123432,432123,212343,432123,123432,321234,432123,343212,234321,123432,123432};
ll a[3][3]={M,0,M,1,1,1,0,0,1},b[3][3],c[3][3];
ll n,ans=0;
void cl()
{
    fo(i,0,2) fo(j,0,2) c[i][j]=a[i][j],a[i][j]=0;
}
void ch()
{
    cl();
    fo(i,0,2) fo(j,0,2) fo(k,0,2) a[i][k]=(a[i][k]+c[i][j]*c[j][k])%mo;
}
void ch2()
{
    cl();
    fo(i,0,2) fo(j,0,2) fo(k,0,2) a[i][k]=(a[i][k]+c[i][j]*b[j][k])%mo;
}
void mi(ll x)
{
    if(x<=1) return;
    fo(i,0,2) fo(j,0,2) b[i][j]=a[i][j];
    mi(x/2);
    ch();
    if(x%2==1) ch2();
}
int main()
{
    scanf("%lld",&n);
    if(n<15) 
    {
        fo(i,1,n) ans=(ans+A[i])%mo;
        printf("%lld\n",ans);return 0;
    }
    ll jy1=n/15,jy2=n%15;
    mi(jy1-1);
    if(jy1==1)
    {
        memset(a,0,sizeof(a));
        a[0][0]=a[1][1]=a[2][2]=1;
    }
    fo(i,0,2) fo(j,0,2) b[i][j]=a[i][j];
    fo(i,1,15)
    {
        memset(a,0,sizeof(a));
        a[0][0]=A[i];a[0][1]=B[i];a[0][2]=A[i];
        ch2();
        ans=(ans+a[0][2])%mo;
        if(i<=jy2) ans=(ans+a[0][0]*M+a[0][1])%mo;
    }
    printf("%lld\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值