Sequence Gym - 103104I (dp + 单调栈优化)

题意

  1. 现在有一个长度为 n 的数组 p,数组元素的取值为 1~n,
  2. 给我们 m 个限制条件 <x, y> ,每个限制为 p [x] != y.
  3. 现在让我求出四元组的 <A,B,L,R> 的数量,四元组的意思是在 p 数组中 [A,B] 子区间的任意数字可以取值为 [L, R] 中的任意一个数字。(A、B、L、R 可以均 <= n)。

思路

  1. 设状态转移数组:f [i][j] 表示以 i 位置为结尾的所有子区间中,以 j 数字结尾所能产生的答案的贡献数量。
  2. 可以用单调栈去维护状态转移。具体看代码吧更好理解

代码

#include <bits/stdc++.h>
using namespace std;
#define db  double
#define ll  long long
#define Pir pair<int, int>
#define fi  first
#define se  second
#define pb  push_back
#define m_p make_pair
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
/*==========ACMer===========*/
const int N = 5005; 
int n, m;
int a[N][N], b[N][N];       //b[i][j] 表示以 i 位置结尾,以 j 数字可以向前延伸多少距离(延伸的距离前缀和)
ll f[N];                    //f[j]    表示:当我统计以 i 位置为结尾的子串中,以 j 数字作为可选区间的右端点的时候所能产生的答案个数有多少,我们要用单调栈去维护答案的状态转移
int st[N], top;             //st      表示:从栈底往上单调递增


int main()
{
    scanf("%d %d", &n, &m);
    int x, y;
    while (m --)
    {
        scanf("%d %d", &x, &y);
        a[x][y] = 1;
    }

    for (int i = 1; i <= n; i ++) {
        for (int j = 1; j <= n; j ++) {
            if (a[i][j]) b[i][j] = 0;
            else b[i][j] = b[i - 1][j] + 1;
        }
    }

    ll ans = 0;
    for (int i = 1; i <= n; i ++) {
        top = 0;
        for (int j = 1; j <= n; j ++) {
            while (top && b[i][j] <= b[i][st[top]]) top --;     //所有比 j 数字能延伸的长的 st[top] 数字,他们的长度是比 j 延伸长度那一部分是没有用的,
                                                                //因为此时是以数字 j 结尾(必须包含数字 j) 所能产生的答案, 因此所有比 j 长的要被弹出去,我们算答案, 
                                                                //而剩余没有被弹出去的部分的延伸长度比 j 小可以直接继承 f[p],因为此时 j 所能延伸再长都是没有用的都是被栈内剩余的元素的长度(比 j 数字所能延伸的长度短)所限制 
            int p = top ? st[top] : 0;
            f[j] = (j - p) * b[i][j] + f[p];
            ans += f[j];
            st[++ top] = j;
        }
    }

    printf("%lld\n", ans);

    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值