[CROC 2016 - Elimination Round E] 矩阵优化dp

题目链接
这种矩阵优化 dp d p 的题原来做过但还是做少了…
不过前面的暴力 dp d p 还是很好想的啊

我们考虑设 f[i] f [ i ] 为以 i i 结尾的子序列方案数,那么转移的时候每次选取最小的f[x], 令 f[x]=1+ni=1f[i] f [ x ] = 1 + ∑ i = 1 n f [ i ] 即可
可以发现这是一个线性的转移方程,那么直接矩乘优化即可
时间复杂度 O(n+k3logm) O ( n + k 3 l o g m )

Codes
#include<bits/stdc++.h>
#define For(i, a, b) for(register int i = a; i <= b; ++ i)
#define PII pair<int, int> 
#define mp make_pair
#define int long long

using namespace std;

const int maxn = 1e6 + 10, mod = 1e9 + 7, maxk = 100 + 3;
int n, m, k, a[maxn], f[maxk], sum, lst[maxk], rk[maxk];

void add(int &x, int y) {
    x += y;
    if(x >= mod) 
        x -= mod;
    if(x < 0) 
        x += mod;
}

void File() {
    freopen("sequence.in", "r", stdin);
    freopen("sequence.out", "w", stdout);
}

void Init() {
    scanf("%lld%lld%lld", &n, &m, &k);
    For(i, 1, n) 
        scanf("%lld", &a[i]);
    For(i, 1, n) {
        lst[a[i]] = i;
        int tmp = f[a[i]];
        f[a[i]] = sum + 1;
        add(sum , f[a[i]]);
        add(sum, -tmp);
    }
}


namespace BF {
    void Solve() {
        priority_queue<PII, vector<PII>, greater<PII> > q;
        For(i, 1, k) q.push(mp(lst[i], i));
        For(i, 1, m) {
            PII x = q.top(); q.pop();
            lst[x.second] = n + i;
            int tmp = f[x.second];
            f[x.second] = sum + 1;
            add(sum, f[x.second]);
            add(sum, -tmp);
            q.push(mp(lst[x.second], x.second));
        }
        printf("%lld\n", sum);
    }
}

namespace Sub7 {

    struct Martix {
        int n, m;
        int a[maxk][maxk];
    }now, zhuan, ret;

    Martix Mul(Martix A, Martix B) {
        Martix res;
        res.n = A.n, res.m = B.m;
        For(i, 1, A.n)
            For(j, 1, B.m) {
                res.a[i][j] = 0;
                For(k, 1, A.m)
                    (res.a[i][j] += A.a[i][k] * B.a[k][j] % mod) %= mod;
            }
        return res;
    }

    void qpow(int x) {
        while(x) {
            if(x & 1) 
                ret = Mul(ret, zhuan);
            x >>= 1, zhuan = Mul(zhuan, zhuan);
        }
    }

    void Solve() {
        priority_queue<PII, vector<PII>, greater<PII> > q;
        For(i, 1, k) q.push(mp(lst[i], i));
        For(i, 1, m) {
            PII x = q.top();
            if(x.first) {
                m -= i - 1;
                break;
            }
            q.pop();
            lst[x.second] = n + i;
            int tmp = f[x.second];
            f[x.second] = sum + 1;
            add(sum, f[x.second]);
            add(sum, -tmp);
            q.push(mp(lst[x.second], x.second));
        }
        now.n = 1, now.m = k + 1, now.a[1][1] = 1;
        For(i, 2, k + 1) {
            PII x = q.top(); q.pop();
            now.a[1][i] = f[x.second];
        }
        zhuan.a[1][1] = 1, zhuan.n = k + 1, zhuan.m = k + 1;
        For(i, 2, k) zhuan.a[i + 1][i] = 1;
        For(i, 1, k + 1) zhuan.a[i][k + 1] = 1;
        ret.n = k + 1, ret.m = k + 1;
        For(i, 1, k + 1) 
            ret.a[i][i] = 1;
        qpow(m + 1);
        now = Mul(now, ret);
        printf("%lld\n", now.a[1][k + 1] - 1);
    }
}

signed main() {
//  File();
    Init();
    if(m <= 1e6) 
        BF::Solve();
    else
        Sub7::Solve();
    return 0;
}

代码有点丑但也不想改了==

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值