算法题-国王无双dp统计方案数

  1. 题目
  2. 输入
  3. 样例
  4. 题解

题目

牛牛最近三国演义入了迷,他时常幻想着自己是一代枭雄,坐拥城池百万,精锐无数。

然而他只能做做美梦玩玩三国无双的游戏罢了 qwq。

游戏中牛牛有 $n$ 座城池,城池编号为 1∼$n$,由于某些城池地理位置极其重要,因此牛牛要设置一些关卡来建立防御工程。现在牛牛有 $m$ 个工程计划,每个工程计划有两个整数 $L$,$R$,表示从第$L$座城池到第 $R$座城池中,至少有一个城池被设置为关卡。

牛牛想知道,为了使得每个计划都满足,有多少中不同的设置关卡的方法?

输入

第一行输入两个整数$n$和 $m$,其中 $n$表示城池的数量,$m$表示工程计划的数量。

接下来 $m$ 行,每行两个整数 $L$,$R$,表示该计划要求从第 $L$座城池到第 $R$ 座城池中,至少有一个城池被设置为关卡。

$1 \leqslant n, m \leqslant 10^5, 1 \leqslant L \leqslant R \leqslant n$.

样例

输入:
3 2
2 3
1 2

输出:
5

提示:
用1表示关卡,0表示普通城池,那么这5种方案分别是: 010,011,110,111,101

输入:
12 3
1 7
6 12
5 10

输出:
3988

题解

状态表示: $f(i)$表示只考虑前$i$个城池, 第$i$个城池设置为关卡的所有方案数.

状态计算: 假设$l_i$表示所有右端点小于等于$i$的区间中, 左端点的最大值 (如果所有区间右端点都大于$i$, 那么$l_i = 0$).

那么$f(0) = 0, f(i) = \sum_{j = l_{i-1}}^{i-1}f(j), i \geqslant 0$

代码如下:

最终统计答案时, 答案是$f(l_n) + f(l_n + 1) + … + f(n)$

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1010, mod = 1e9 + 7;

typedef long long LL;

int n, m;
LL f[N];
int lp[N];


int main()
{
    cin >> n >> m;

    for (int i = 0; i < m; i ++)
    {
        int l, r;
        cin >> l >> r;
        lp[r] = max(lp[r], l);
    }

    f[0] = 1;
    int l = 0;

    for (int i = 1; i <= n; i ++)
    {
        for (int j = l; j <= i - 1; j ++) f[i] = (f[i] + f[j]) % mod;
        l = max(l, lp[i]);

    }

    LL ans = 0;
    for (int i = l; i <= n; i ++) ans = (ans + f[i]) % mod;

    cout << ans << endl;

    return 0;
}

一种优化方法: 前缀和

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1010, mod = 1e9 + 7;

typedef long long LL;

int n, m;
LL s[N];
int lp[N];


int main()
{
    cin >> n >> m;

    for (int i = 0; i < m; i ++)
    {
        int l, r;
        cin >> l >> r;
        lp[r] = max(lp[r], l);
    }

    s[0] = 1;
    int l = 0;

    for (int i = 1; i <= n; i ++)
    {
        if (l > 0) s[i] = s[i - 1] + s[i - 1] - s[l - 1];
        else s[i] = s[i - 1] + s[i - 1];
        l = max(l, lp[i]);

    }

    LL ans = 0;
    if (l > 0) ans = s[n] - s[l - 1];
    else ans = s[n];

    cout << ans << endl;

    return 0;
}