首页 > 代码库 > ZOJ 2671 Cryptography 矩阵乘法+线段树

ZOJ 2671 Cryptography 矩阵乘法+线段树

B - Cryptography
Time Limit:5000MS     Memory Limit:32768KB     64bit IO Format:%lld & %llu
Submit Status Practice ZOJ 2671

Description

Young cryptoanalyst Georgie is planning to break the new cipher invented by his friend Andie. To do this, he must make some linear transformations over the ring Zr = Z/rZ.

Each linear transformation is defined by 2×2 matrix. Georgie has a sequence of matrices A1 , A2 ,..., An . As a step of his algorithm he must take some segment Ai , Ai+1 , ..., Aj of the sequence and multiply some vector by a product Pi,j=Ai × Ai+1 × ... × Aj of the segment. He must do it for m various segments.

Help Georgie to determine the products he needs.

Input

There are several test cases in the input. The first line of each case contains r ( 1 <= r <= 10,000), n ( 1 <= n <= 30,000) and m ( 1 <= m <= 30,000). Next n blocks of two lines, containing two integer numbers ranging from 0 to r - 1 each, describe matrices. Blocks are separated with blank lines. They are followed by m pairs of integer numbers ranging from 1 to n each that describe segments, products for which are to be calculated. 
There is an empty line between cases.

Output

Print m blocks containing two lines each. Each line should contain two integer numbers ranging from 0 to r - 1 and define the corresponding product matrix.
There should be an empty line between cases.

Separate blocks with an empty line.

Sample

InputOutput
3 4 40 10 02 11 20 00 21 00 21 42 31 32 2
0 20 00 20 10 10 02 11 2

 

题意是给出n个矩阵,编号是从1到n,再给m个查询,每个查询给定l和r,输出第l个矩阵连成到第r个矩阵的积,每次乘法操作后都要对每个数对r求余。

思路很容易想到用线段树,保存下中间的变量,下次查询再需要用到的时候可以直接返回这一个结果,时间复杂度o(mlogn)。网络上很多这题题解了,那我就贴一个zkw版的吧。需要注意的是,矩阵乘法不满足交换律,只能第l个乘第l+1个一直乘到第r个,但是zkw的线段树,是先遇到第l个和第r个,然后遇到第l+1和r-1、l+2和r-2一直到l跟r在同一层,所以顺序要有点改变,我使用了vector,但相比起传统线段树还是时间还是少了不少。

 

代码

#include <iostream>#include <cstdio>#include <cstring>#include <vector>#define lson l,mid,rt<<1#define rson mid+1,r,rt<<1|1using namespace std;const int N = 30010;int r,n,m,M;struct _matrix{    int mat[3][3];    _matrix operator * (const _matrix &b) const {        _matrix res;        for(int i=0;i<2;++i)        for(int j=0;j<2;++j)        {            int sum=0;            for(int k=0;k<2;++k)    sum+=mat[i][k]*b.mat[k][j];            res.mat[i][j]=sum%r;        }        return res;    }    _matrix operator *= (const _matrix &b)  {        return *this = (*this) * b;    }    void clear()    {memset(mat,0,sizeof(mat));for(int i=0;i<2;++i) mat[i][i]=1;}    void in()   {scanf("%d%d%d%d",&mat[0][0],&mat[0][1],&mat[1][0],&mat[1][1]);}    void out()  {printf("%d %d\n%d %d\n",mat[0][0],mat[0][1],mat[1][0],mat[1][1]);}};_matrix res[4*N];void build(int x){    _matrix tmp;    tmp.in();    for(x+=M;x;x>>=1)   res[x] *= tmp;}vector<int> vi;_matrix query(int x,int y){    _matrix ans;    ans.clear();    vi.clear();    int l=x+M-1,r=y+M+1;    for(x=l,y=r;x^y^1;x>>=1,y>>=1)//注意顺序        if(~x&1) ans*=res[x^1];    for(x=l,y=r;x^y^1;x>>=1,y>>=1)        if(y&1)            vi.push_back(y^1);    for(int i=vi.size()-1;i>=0;--i)        ans *= res[vi[i]];    return ans;}bool fir2=1;void run(){    if(fir2) fir2=0;    else puts("");    for(M=1;M<=n;M+=M);    for(int i=0;i<=M+n;++i) res[i].clear();    for(int i=1;i<=n;++i)        build(i);    int l,r;    bool fir=1;    while(m--)    {        if(fir) fir=0;        else puts("");        scanf("%d%d",&l,&r);        query(l,r).out();    }}int main(){    while(scanf("%d%d%d",&r,&n,&m)!=EOF)        run();    return 0;}