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);
}