HDU 5068 Harry And Math Teacher 线段树维护矩阵乘积

题意:相邻的两层楼之间有两个门,每个门内都有一个楼梯,分别通向上一层的两扇门。但是,门内的楼梯会转换状态。所有楼梯起始都是通的。在状态转后后,会从通变成不通,反之也可以。如果楼梯是不通的,那就不能从该楼梯到上一层。有以下两种操作:1.修改X层第Y个门通向上一层的第Z个门的楼梯的状态。2.查询从第X层到第Y层的所有的方案数。

思路:因为查询的是方案数,我们想到了DP。但是,题目中的是动态修改动态查询,怎么办呢?因为是线性递推,我们可以利用矩阵来计算方案数。

            同时,因为矩阵满足结合律,我们就可以利用线段树来保存成段的矩阵乘积。而修改就是单点修改了。

注意:虽然题目中明确要求用取模运算,但是在计算的过程中还是会爆int的,所以要用long long.

代码如下:

#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

typedef long long ll;

const ll MOD = 1000000007;
const int MAX = 50010;


struct Matrix{
    ll a[2][2];
    Matrix(){memset(a,0,sizeof(a));}

    Matrix operator * (const Matrix & A) const{
        Matrix C;
        for(int i = 0; i < 2; ++i)
            for(int j = 0; j < 2; ++j)
                for(int k = 0; k < 2; ++k)
                    C.a[i][j] = (C.a[i][j] + a[i][k] * A.a[k][j]) % MOD;
        return C;
    }
};
#define lson(o) (o<<1)
#define rson(o) ((o<<1)|1)

struct yy{
    Matrix m;
    int l,r;
} node[MAX<<3];

void pushup(int o)
{
    node[o].m = node[lson(o)].m * node[rson(o)].m;
}

void build(int l, int r, int o)
{
    node[o].l = l;
    node[o].r = r;
    if(l == r){
        for(int i = 0; i < 2; ++i)
            for(int j = 0; j < 2; ++j)
                node[o].m.a[i][j] = 1;
        return;
    }

    int mid = (l + r) >>1;
    build(l,mid,lson(o));
    build(mid+1,r,rson(o));
    pushup(o);
}
int x,y,z;

void update(int x,int o)
{
    if(node[o].l == node[o].r){
        node[o].m.a[y][z] ^= 1;
        return;
    }
    int mid = (node[o].l + node[o].r) >> 1;
    if(x <= mid)
        update(x,lson(o));
    else
        update(x,rson(o));
    pushup(o);
}

Matrix query(int L,int R,int o)
{
    if(L <= node[o].l && R >= node[o].r)
        return node[o].m;

    Matrix ans;
    ans.a[0][0] = ans.a[1][1] = 1;

    int mid = (node[o].l + node[o].r) >> 1;
    if(L <= mid)
        ans = ans * query(L,R,lson(o));
    if(R > mid)
        ans = ans * query(L,R,rson(o));
    return ans;
}

int main(void)
{
    //freopen("in","r",stdin);
    int N,M;
    while(~scanf("%d%d", &N,&M)){
        build(1,N-1,1);
        for(int i = 0; i < M; ++i){
            int Q;
            scanf("%d", &Q);
            if(Q==1){
                scanf("%d %d %d", &x,&y,&z);
                y--,z--;
                update(x,1);
            }
            else{
                scanf("%d %d", &x,&y);
                Matrix ret = query(x,y-1,1);
                ll ans = 0;
                for(int i = 0; i < 2; ++i)
                    for(int j = 0; j < 2; ++j)
                        ans = (ans + ret.a[i][j]) % MOD;
                printf("%I64d\n",ans);
            }
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值