zoukankan      html  css  js  c++  java
  • 【高手训练】【树状数组】导线问题

    题目

    哈蒙有(n)条导线排成一排,每条导线有一个电阻值,神奇的电光只能从一根导线传到电阻比它大的上面,而且必须从左边向右传导,当然导线不必是连续的。

    哈蒙想知道电光最多能通过多少条导线,还想知道这样的方案有多少。

    Solution

    简化题意:LIS及其方案数,(a_i leq n, n leq 100010)

    最长上升子序列(n^2)显然很好做,DP入门题。我们再考虑一下如何实现(n log_2n)

    我们可以用一个什么数据结构维护,比如树状数组。

    对于一个需要转移的(i),我们要找的是在(i)之前的(j),且 满足(a_j < a_i)(f_j)的最大值。

    树状数组从左至右加入本就保证(j)(i)之前,以(a_i)为下标建权值树状数组,维护(f_i)前缀最大值,这样找出来的(f_j)必定合法且最优,直接转移即可。

    方案数也没什么问题。

    观察一个基本的数据:

    (a_i) LIS 方案数
    2 1 1
    1 1 1
    4 2 2
    3 2 2
    5 3 4

    我们可以理解为

    最长上升子序列方案数必定是每层个数的累乘, 而本质就是对于某个长度累加每个方案数,得到下一值的方案数。

    而我们只需要在转移的时候把前缀最大值改成累加,注意方案数随最优值更新。

    (mathrm{Code:})

    #include <bits/stdc++.h>
    
    const int mod = 123456789;
    const int N   = 200010;
    int n, m;
    int a[N] = {};
    
    inline int add(int a, int b) { return a + b >= mod ? a + b - mod : a + b; }
    inline void Add(int &a, int b) { a = add(a, b); }
    template <class T>
    void read(T &s) {
        T w    = 1;
        char c = getchar();
        while ((c < '0' || c > '9') && c != '-') c = getchar();
        if (c == '-') w = -1, c = getchar();
        while (c <= '9' && c >= '0')
            s = (s << 1) + (s << 3) + c - '0', c = getchar();
        s *= w;
    }
    template <class T>
    void write(T x) {
        if (x < 0) x = ~x + 1, putchar('-');
        if (x > 9) write(x / 10);
        putchar(x % 10 + 48);
        return void();
    }
    
    int f[N] = {}, s[N] = {};
    struct BIT {
        int c[N], c1[N];
        inline void inc(int x, int v, int s) {
            for (++x; x <= N - 10; x += x & (-x)) {
                if (v == c[x]) Add(c1[x], s);
                if (c[x] < v) c[x] = v, c1[x] = s;
            }
        }
        inline int ask(int x) {
            int maxn = -1;
            for (++x; x; x -= x & (-x)) maxn = std ::max(maxn, c[x]);
            return maxn;
        }
        inline int Ask(int x) {
            int sum = 0, now = 0;
            for (++x; x; x -= x & (-x)) {
                if (c[x] == now) Add(sum, c1[x]);
                if (c[x] > now) now = c[x], sum = c1[x];
            }
            return sum;
        }
    } T;
    
    main() {
        int maxx = 0;
        read(n);
        read(m);
        for (int i = 1; i <= n; ++i) read(a[i]);
        f[1] = 1;
        s[1] = 1;
        T.inc(a[1], f[1], s[1]);
        for (int i = 2; i <= n; ++i) {
            f[i] = T.ask(a[i] - 1) + 1;
            s[i] = T.Ask(a[i] - 1);
            if (f[i] == 1) s[i] = 1;
            T.inc(a[i], f[i], s[i]);
            maxx = std ::max(maxx, f[i]);
        }
        write(maxx);
        putchar(10);
        if (m == 0) return 0;
        int ans = 0;
        for (int i = 1; i <= n; ++i)
            if (f[i] == maxx) Add(ans, s[i]);
        write(ans);
        return 0;
    }
    
  • 相关阅读:
    sql排序对比(row_number,rank,dense_rank)
    SQL分组排名+行转列
    MS SQL 权限设置脚本
    centos8容器中安装lamp及wordpress
    MacOS禁止向日葵开机启动
    docker(1)
    centos7的firewalld
    ssh免密码
    CENTOS7安装vsftp
    centos 7 安装samba配置匿名共享文件夹
  • 原文地址:https://www.cnblogs.com/yywxdgy/p/13099443.html
Copyright © 2011-2022 走看看