其他分享
首页 > 其他分享> > (SEERC 2020) n'jienjieF. Fence Job

(SEERC 2020) n'jienjieF. Fence Job

作者:互联网

原题链接:https://codeforces.com/gym/103102/problem/F

题目大意:给出1~n的一个排列h1,...,hn,每次操作选取区间[l,r],并把h[l],...h[r]同时变为min{h[l],...h[r]}。问在若干(>=0)次操作后,可以得到多少种不同的序列。

分析:

操作的先后顺序不可互换,不同的操作序列也可以得到相同的结果,这提示我们不应该考虑如何在原序列上进行操作,应该考虑哪些结果是合法的;

可以把操作想象成选择区间[l,r]内最小的数,然后把它向两侧延展覆盖整个区间。于是,我们可以考虑每个h[i]可以延展的区间的边界。结论是,每个h[i]可以向左右延展到第一个小于h[i]的数前;在每一个合法的结果中,h[i]只能出现在上述区域;

分析到这里,通常的做法有DP和组合数计算,这个题里不同h[i]的区间延展相互制约,不宜直接计算组合数,故考虑DP;

n=3000,猜测是O(n^2)的DP,每个DP的含义是满足某种性质的合法不同结果的数量;

dp[i][j]表示仅用h[1],...,h[i]能够得到的长度为j(左对齐)的合法不同结果数,初值dp[0][0]=1;

i时,设h[i]的可行区间为(l,r),于是只需要更新(l,r)内的dp,其他直接继承i-1的结果,于是我们可以省略i这一维度;

对l+1<=k<=r-1,依次使让dp[k]+=dp[k - 1],即加上第k位是h[i]的合法结果(此时前k-1位由h[1..i]组成);

对k>=r,如果我们想让前面出现h[i],那么此时h[1..i-1]延展到第k位的情况,所以dp[k]仍然只能由h[1..i-1]组成;

 

代码:

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

const int INF = 0x3f3f3f3f;

typedef long long LL;
typedef unsigned long long ULL;

const int P = 1e9 + 7;

const int maxn = 3000 + 10;
int h[maxn], n;
LL dp[maxn];

int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) scanf("%d", &h[i]);

    
    dp[0] = 1;
    for(int i = 1; i <= n; i++)
    {
        int l, r; l = r = i;
        while(l >= 1 && h[l] >= h[i]) --l;
        while(r <= n && h[r] >= h[i]) ++r;
        for(int k = l + 1; k <= r - 1; k++)
        {
            dp[k] += dp[k - 1];
            dp[k] %= P;
        }
    }
    printf("%lld", dp[n]);

    return 0;
}

 

标签:...,DP,int,long,Job,2020,延展,jienjieF,dp
来源: https://www.cnblogs.com/wangxiaoge2001/p/15002565.html