CF256E Lucky Arrays

在线段树上dp。我无论如何也是想不出来的。

我直接说思路吧。就是建一颗[1, n]的线段树,节点域为a[3][3],表示该节点表示的区间首尾分别为x, y时方案数为a[x][y]。叶子节点的a数组的值与于该点的值有关:叶子节点的值z为0时有a[i][j] = (i == j ? 1 : 0),否则只有a[z][z]=1其余为0。那么其它节点的维护可以用如下函数写出:

void update(seg &lch, seg &rch)   {
    for(int l = 0; l < 3; l++)  {
        for(int r = 0; r < 3; r++)  {
            a[l][r] = 0;
            for(int i = 0; i < 3; i++)
                for(int j = 0; j < 3; j++)
                    if(matrix[i][j])
                        a[l][r] = (1LL * lch.a[l][i] * rch.a[j][r] + a[l][r]) % MOD;
        }
    }
}

这个函数我写在了结构体内部,为了方便我从0开始编号,matrix就是输入的那个矩阵

其他就很简单了,直接上代码。

#include<bits/stdc++.h>
using namespace std;

const int MAXN = 77777;
const int MOD = 777777777;

int matrix[3][3];

struct seg  {
    int l, r;
    int a[3][3];
    void update(seg &lch, seg &rch)   {
        for(int l = 0; l < 3; l++)  {
            for(int r = 0; r < 3; r++)  {
                a[l][r] = 0;
                for(int i = 0; i < 3; i++)
                    for(int j = 0; j < 3; j++)
                        if(matrix[i][j])
                            a[l][r] = (1LL * lch.a[l][i] * rch.a[j][r] + a[l][r]) % MOD;
            }
        }
    }
}   Tree[MAXN * 4 + 10];

void Build(int u, int l, int r)    {
    Tree[u].l = l;  Tree[u].r = r;
    if(l >= r)  {
        int (*a)[3] = Tree[u].a;
        a[0][0] = a[1][1] = a[2][2] = 1;
        return ;
    }
    int mid = (l + r) / 2;
    Build(u * 2, l, mid);
    Build(u * 2 + 1, mid + 1, r);
    Tree[u].update(Tree[u * 2], Tree[u * 2 + 1]);
}

void Ins(int u, int x, int p)  {
    if(Tree[u].l == Tree[u].r)  {
        memset(Tree[u].a, 0, sizeof(Tree[u].a));
        if(x != 0)  Tree[u].a[x - 1][x - 1] = 1;
        else {
            int (*a)[3] = Tree[u].a;
            a[0][0] = a[1][1] = a[2][2] = 1;
        }
        return ;
    }
    if(Tree[u * 2].r >= p)  Ins(u * 2, x, p);
    else Ins(u * 2 + 1, x, p);
    Tree[u].update(Tree[u * 2], Tree[u * 2 + 1]);
}

int work(int p, int x)   {
    Ins(1, x, p);
    int res = 0, (*a)[3] = Tree[1].a;
    for(int i = 0; i < 3; i++)
        for(int j = 0; j < 3; j++)
            res = (res + a[i][j]) % MOD;
    return res;
}

int main()  {
    int n, ops;
    scanf("%d%d", &n, &ops);
    for(int i = 0; i < 3; i++)
        for(int j = 0; j < 3; j++)
            scanf("%d", &matrix[i][j]);

    Build(1, 1, n);
    for(int i = 1; i <= ops; i++)   {
        int p, x;
        scanf("%d%d", &p, &x);
        printf("%d\n", work(p, x));
    }

    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值