bzoj2326:[HNOI2011]数学作业(分段矩阵乘法)

      题目大意:输入n(n<=10^18)和m,将1~n的整数连起来模m输出,比如n=13则输出12345678910111213模m的数。

      设f[i]为1~i整数连起来模m的数,i的位数为k,则有f[i]=(f[i-1]*10^k+i)mod m。可以发现f[i-1]和10^k都是会变化的,不能直接矩乘,这就尴尬了>_<。但是仔细想想(跑去问CZL),其实可以分段来矩乘,把k一样的数矩乘(1..9一样,10..99一样,100..999一样)就行了,这样就变成了ax+by+c的形式,b=1,c=0,然后魔改

      orz CZL初二的时候1A,然而我却WA了好几次才AC。。。自从被模的巨大常数坑过之后就不敢多用取模了,最后全加了居然就过了,坑爹的取模T_T。

真的写的非常丑的代码如下:

type
  poi=array[1..3,1..3]of qword;
var
  map,g,c:poi;
  f:array[1..3]of qword;
  n,p,t,tt:qword;
  i,j,k,num:longint;

procedure merge(var a,b:poi);
var
  i,j,k:longint;
begin
  fillchar(c,sizeof(c),0);
  for i:=1 to 3 do
  for j:=1 to 3 do
  for k:=1 to 3 do
  c[i,j]:=(c[i,j]+((a[i,k] mod p)*(b[k,j] mod p))mod p)mod p;
  move(c,a,sizeof(c));
end;

procedure qp(n:int64);
var
  i,j,k:longint;
begin
  if tt<>1 then
  begin
    fillchar(map,sizeof(map),0);
    fillchar(g,sizeof(g),0);
  end;
  map[1,1]:=tt*10;map[1,2]:=1;map[2,2]:=1;map[2,3]:=1;map[3,3]:=1;
  g[1,1]:=1;g[2,2]:=1;g[3,3]:=1;
  while n<>0 do
  begin
    if n and 1=1 then merge(g,map);
    merge(map,map);
    n:=n>>1;
  end;
  fillchar(c,sizeof(c),0);
  for i:=1 to 3 do
  for j:=1 to 1 do
  for k:=1 to 3 do
  c[i,j]:=(c[i,j]+(g[i,k]*f[k])mod p)mod p;
  for i:=1 to 3 do
  f[i]:=c[i,1];
end;

begin
  readln(n,p);
  f[2]:=1;f[3]:=1;g[1,1]:=1;g[2,2]:=1;g[3,3]:=1;
  map[1,2]:=1;map[2,2]:=1;map[2,3]:=1;map[3,3]:=1;
  t:=1;num:=1;
  while n>=t do
  begin
    inc(num);
    t:=t*10;
  end;
  dec(num);
  tt:=1;
  for i:=1 to num do
  begin
    if i=num then qp(n-tt+1)
    else qp(tt*10-tt);
    tt:=tt*10;
  end;
  writeln(f[1]);
end.
View Code
原文地址:https://www.cnblogs.com/Sakits/p/5789297.html