高精度矩阵

黑书的2.1的例题《细菌》改编而来。求三个操作进行若干次之后的结果,因为N很大,模拟肯定是不行的,
这里所给的三个操作都是线性操作,因此可以用矩阵乘法来做,最初的向量是(C1, C2, 1)。
首先读入操作,每一个操作都可以变换为一个3x3的矩阵,所有指令是这些矩阵按顺序的乘积,
再求出矩阵的N次幂,二分求幂即可,最后用(C1, C2, 1)乘上这个矩阵,取C2部分的值。
其中高精度数注意移位和模2的处理,移位用高精度除单精度来写的,模2的话标程用的是10^8进制,只要拿最后一位模2就行了。

备注:
SET C1, C2相当于乘上矩阵:
 0 0 0
 1 1 0
 0 0 1
SET C1, number相当于:
        0         0 0
        0         1 0
        number    0 1
ADD C1, C2相当于乘上矩阵:
 1 0 0
 1 1 0
 0 0 1
ADD C1, number相当于:
        1         0 0
        0         1 0
        number    0 1
MUL C1, number 相当于
 number 0 0
 0      1 0
        0      0 1
#include <cstdio>//高精度数矩阵乘法
#include <cstdlib>
#include <cstring>
#include <algorithm>


const int mod = 1000000007;
int T;
int C1, C2;

int str_to_int(const char *str){
  int k;
  sscanf(str, "%d", &k);
  return k;
}


const struct Mat{
  long long  a[3][3];
} I = {{{1,0,0}, {0,1,0}, {0,0,1}}};

struct Bignum_t{
  int data[400];
  int len;
  static const int piece = 100000000;
  static const int half  =  piece >> 1;
  Bignum_t(int k){
    memset(data, 0, sizeof(data));
    data[len = 0] = k;
  }
  Bignum_t(char *str){
    int n = strlen(str);
    int start = n - 8;
    len = -1;
    while(start > 0){
      data[++len] = str_to_int(&str[start]);
      str[start]=0;
      start-= 8;
    }
    if (start < 0) {
      start = 0;
         data[++len] = str_to_int(&str[start]);
    }
  }
  int rbit() const {    //the last bit
    return data[0] & 1;
  }
  void rshift_inplace(){//n>>1;
    int carry = 0;
    for(int i=len; i>=0; --i){
      carry *= piece;
      int newcarry = (data[i] + carry) & 1;
      data[i] = (data[i] + carry) >> 1;
      carry = newcarry;
    }
    while(data[len] == 0 && len > -1) --len;
  }
  void print(){
    printf("%d", data[len]);
    for(int i=len-1; ~i; --i)
      printf("%08d", data[i]);
    puts("");

  }
};

Mat unit;
Mat mul(Mat a, Mat b){
  Mat c = {};
  for(int i=0; i<3; ++i)
    for(int j=0; j<3; ++j){
      long long val = 0;
      for(int k=0; k<3; ++k){
        val += a.a[i][k] % mod * b.a[k][j] % mod;
        if (val >= mod) val %= mod;
      }
      c.a[i][j] = val % mod ; 
    }
  return c;
}

Mat pwr(Mat a, Bignum_t &b){
  Mat d = I, c = a;
  while(~b.len){
    if (b.rbit()) d = mul(d, c);
    c = mul(c, c);
    b.rshift_inplace();
  }
  return d;
}

char s1[10], s2[10], s3[10], s[40];
Mat gen_mat(int col, int x1, int x2, int x3){ //=>assemble matrix?
  Mat p = I;
  p.a[0][col] = x1 % mod;
  p.a[1][col] = x2 % mod;
  p.a[2][col] = x3 % mod;
  return p;
}

void compile(){
  int col = s2[1] - '1'; //C1 => 0, C2 => 1
  int val;
  int op2;
  switch(s1[0]){   
    case 'M':
      val = str_to_int(s3);
      unit = mul(unit, gen_mat(col, !col * val, col * val, 0));
      break;
    case 'S':
      if(s3[0]=='C'){
        op2 = s3[1]-'1';//C1 => 0, C2 => 1
        unit = mul(unit, gen_mat(col, !op2, op2, 0));
      }else{
        val = str_to_int(s3);
        unit = mul(unit, gen_mat(col, 0, 0, val));
      }
      break;
    case 'A':
      if(s3[0]=='C'){
        op2 = s3[1]-'1';//C1 => 0, C2 => 1
        unit = mul(unit, gen_mat(col, !op2 + !col, op2 + col, 0));
      }else{
        val = str_to_int(s3);
        unit = mul(unit, gen_mat(col, !col, col, val));
      } 
      break;
    default:
      ;
  }
}


void to_compile(){
  scanf("%d\n", &C1);
  C2 = 0;
  unit = I;
  while(strcmp(gets(s), "END")){
    sscanf(s, "%s %s%s", s1, s2, s3);
    compile();
  }
}

char str[1024];
int ncase = 0;
void to_run(){ 
  printf("Case %d:\n", ++ncase);
  int Q;
  scanf("%d\n", &Q);
  while(Q--){
    Bignum_t x = gets(str);
    Mat q = pwr(unit, x);
    //C1 * q[0][1] + C2 * q[1][1] + 1 * q[2][1]
    long long ans = C1 * q.a[0][1] % mod + q.a[2][1] % mod;//c2=0;
    printf("%d\n", (int)(ans % mod));
  }
}

void go(){
  to_compile();
  to_run();
}

int main(){ 
     //   freopen("C:\\stdin1.txt", "r", stdin);
     //   freopen("C:\\stdout1.txt", "w", stdout);
     freopen("data.in","r",stdin);
     freopen("1003.out","w",stdout);
  scanf("%d", &T);
  while(T--) {
    go();
  }
 // fprintf(stderr, "here");
 // while(1);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值