• 【训练题71:动态规划】Building Blocks | Gym102822B


    【训练题71:动态规划】Building Blocks | Gym102822B

    题意

    在这里插入图片描述

    • 现在定义正左视图,看到的就是这样的
      在这里插入图片描述
    • 给定 n , m n,m n,m 表示俯视图的矩阵的行和列
      给定 k k k 个条件,第 i i i 个条件表示俯视图的第 x i x_i xi 行第 y i y_i yi 列的位置的高为 h i h_i hi
      给定正左视图的每个位置的高度 a i a_i ai。注意到一共有 n + m n+m n+m 个位置
      问你所有合法情况的方案数取模 1 e 9 + 7 1e9+7 1e9+7
      当然满足若某个位置有方块,它的底下一定有方块。
      还要满足对于俯视图的每个位置,必须至少有一个方块。
    • 1 ≤ n , m , k ≤ 1 0 5 1\le n,m,k\le 10^5 1n,m,k105
      1 ≤ a i , h i ≤ 1 0 9 1\le a_i,h_i\le 10^9 1ai,hi109
      保证每个条件的位置均不同。

    思路

    • 注意到,正左视图的第 i i i 个位置影响俯视图两个斜列的位置。
      我们肯定按俯视图一个斜列一个斜列去计算,这样复杂度可以达到 O ( n + m ) O(n+m) O(n+m)
      首先是处理俯视图位置 ( i , j ) (i,j) (i,j) 是正左视图第几个斜列,容易得到为 i + j − 1 i+j-1 i+j1,记作 f ( i , j ) = i + j − 1 f(i,j)=i+j-1 f(i,j)=i+j1
      还要处理一下每个斜列还剩下有多少个位置没有填,记作 c n t [ i ] cnt[i] cnt[i]
    • 注意到,我们考虑当前斜列
      在这里插入图片描述
    • 这一斜列用对角线一划,可以看到对应了正左视图的两个高度,记作 a , b a,b a,b
      自然需要满足的,就是这个斜列位置的最高高度应该为 min ⁡ ( a , b ) \min(a,b) min(a,b)
      当然还有,就是对于 a a a 所对应的两个斜列的最大值为 a a a,对 b b b 同理。

      可以感觉到, a a a 还被上一斜列所影响。
      那么很自然就可以想到一个动态规划
      定义 d p [ x ] [ 0 ] dp[x][0] dp[x][0] 表示考虑完前 x x x 个斜列,最后一个斜列的最大值还没有取到,或者说还没有满足
      d p [ x ] [ 1 ] dp[x][1] dp[x][1] 表示考虑完前 x x x 个斜列,最后一个斜列的最大值取到了,或者说满足了
    • 首先考虑一些非法的情况
      因为有 k k k 个已知条件,这个位置的高度就不能大于它对应正左视图的两个位置的值
      h > a [ f ( x , y ) ] h>a[f(x,y)] h>a[f(x,y)] h > a [ f ( x , y ) + 1 ] h>a[f(x,y)+1] h>a[f(x,y)+1] 自然非法
      然后对于初始化,即第一斜列我们单独考虑
      第一斜列就是位置 ( 1 , 1 ) (1,1) (1,1)
      如果 a [ 1 ] > a [ 2 ] a[1]>a[2] a[1]>a[2] 一定是不行的,因为 a [ 1 ] a[1] a[1] 就是 ( 1 , 1 ) (1,1) (1,1) 处的高度,那么 a [ 2 ] a[2] a[2] 就至少是 a [ 1 ] a[1] a[1]
      如果 ( 1 , 1 ) (1,1) (1,1) 的位置是已知条件的话,那么 h ( 1 , 1 ) h_{(1,1)} h(1,1) 当然必须等于 a [ 1 ] a[1] a[1]
    • 接下来考虑递推转移
      • 首先,假设上一个状态为 d p [ i − 1 ] [ 1 ] dp[i-1][1] dp[i1][1],即 a a a 已经满足了
        • 如果我们希望 b b b 也满足,即转移到 d p [ i ] [ 1 ] dp[i][1] dp[i][1],那么需要 b ≤ a b\le a ba,不然就破坏了正左视图上一位置最大值为 a a a 的条件
          • 如果这一斜列有高度 b b b 了,那么剩余 c n t cnt cnt 个位置, 1 ∼ b 1\sim b 1b 随意填,方案数 b c n t b^{cnt} bcnt
          • 如果这一斜列没有高度 b b b那么我们需要满足 c n t > 0 cnt>0 cnt>0,这些位置可以填 1 ∼ b 1\sim b 1b,且满足至少有一个 b b b
            简单容斥,方案数即为 b c n t − ( b − 1 ) c n t b^{cnt}-(b-1)^{cnt} bcnt(b1)cnt
            但是如果 c n t = 0 cnt=0 cnt=0,那么方案数为 0 0 0,不影响
        • 如果我们希望转移到 d p [ i ] [ 0 ] dp[i][0] dp[i][0],即目前的 b b b 暂时不取到
          • 如果 b ≤ a b\le a ba 并且这一斜列没有高度 b b b,才是可以的,每个位置 1 ∼ ( b − 1 ) 1\sim (b-1) 1(b1) 随便选
            方案数为 ( b − 1 ) c n t (b-1)^{cnt} (b1)cnt
          • 如果 b > a b>a b>a,那么我们这个位置可以选择的数为 1 ∼ a 1\sim a 1a,注意不能破坏最大值为 a a a 的限制
            方案数为 a c n t a^{cnt} acnt
      • 如果上一个状态为 d p [ i − 1 ] [ 0 ] dp[i-1][0] dp[i1][0],那么我们 a a a 就一定要满足,不然就非法了。
        • 如果 a > b a>b a>b,我们最大值要放 a a a 但是又不能超过 b b b,自然无解
        • 如果 a < b aa<b,我们满足了 a a a 自然 b b b 暂时不能满足,即转移到 d p [ i ] [ 0 ] dp[i][0] dp[i][0]
          • 如果当前斜列有元素 a a a,那么我们自然满足了条件 a a a,且我们能放的元素为 1 ∼ a 1\sim a 1a,即 b b b 无法目前满足
            方案数就是 a c n t a^{cnt} acnt
          • 如果当前斜列没有元素 a a a,那么我们能放的元素为 1 ∼ a 1\sim a 1a且至少有一个 a a a
            同上方案数为 a c n t − ( a − 1 ) c n t a^{cnt}-(a-1)^{cnt} acnt(a1)cnt
            自然需要满足 c n t > 0 cnt>0 cnt>0
            但是如果 c n t = 0 cnt=0 cnt=0,那么方案数为 0 0 0,不影响
        • 如果 a = b a=b a=b,那么自然满足了 a a a 也同时满足了 b b b,即转移到 d p [ i ] [ 1 ] dp[i][1] dp[i][1]
          转移方案和 a < b aa<b 相同,只不过是转移到 d p [ i ] [ 1 ] dp[i][1] dp[i][1]
    • 可以看到,我们只要先预处理一下第 i i i 斜列是否存在元素 x x x ,记一个 ump 即可

    代码

    • 时间复杂度: O ( n + m ) O(n+m) O(n+m)
    #include 
    #define IOS ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
    using namespace std;
    typedef long long ll;
    void show(){std::cerr << endl;}template<typename T,typename... Args>void show(T x,Args... args){std::cerr << "[ " << x <<  " ] , ";show(args...);}
    
    const int MAX = 2e5+50;
    const int MOD = 1e9+7;
    const int INF = 0x3f3f3f3f;
    const ll LINF = 0x3f3f3f3f3f3f3f3f;
    const double EPS = 1e-5;
    
    ll qpow(ll a,ll n){/* */ll res = 1LL;while(n){if(n&1)res=res*a%MOD;a=a*a%MOD;n>>=1;}return res;}
    ll qpow(ll a,ll n,ll p){a%=p;ll res = 1LL;while(n){if(n&1)res=res*a%p;a=a*a%p;n>>=1;}return res;}
    ll npow(ll a,ll n){/* */ll res = 1LL;while(n){if(n&1)res=res*a;a=a*a;n>>=1;if(res<0||a<0)return 0;}return res;}
    ll inv(ll a){/* */return qpow(a,MOD-2);}
    ll inv(ll a,ll p){return qpow(a,p-2,p);}
    
    unordered_map<int,bool>M[MAX];
    int aa[MAX];
    int cnt[MAX];
    ll dp[MAX][2];
    
    int F(int n,int m){
        return n + m - 1;
    }
    
    int main()
    {
        int T;
        scanf("%d",&T);
        for(int kase = 1;kase <= T;++kase){
            ll ans = 0;
            int n,m,k;
            scanf("%d%d%d",&n,&m,&k);
            int min_nm = min(n,m);
    
            for(int i = 1;i <= n + m;++i){
                M[i].clear();
            }
            for(int i = 1;i <= min_nm;++i){
                cnt[i] = i;
            }
            for(int i = min_nm;i <= n + m - 1;++i){
                cnt[i] = min_nm;
            }
            for(int i = 1;i <= min_nm;++i){
                cnt[n + m - i] = i;
            }
    
    
            for(int i = 1;i <= n + m;++i){
                scanf("%d",&aa[i]);
            }
    
            bool cant = false;
    
            for(int i = 1;i <= k;++i){
                int x,y,h;
                scanf("%d%d%d",&x,&y,&h);
                int aim = F(x,y);
                M[aim][h] = 1;
                cnt[aim]--;
                if(h > aa[aim] || h > aa[aim+1]){
                    cant = true;
                    
                }
            }
    
            if(aa[1] > aa[2]){
                cant = true;
            }
            if(cnt[1] == 0 && !M[1][aa[1]]){
                cant = true;
            }
    
            if(!cant){
                // init
                dp[1][1] = dp[1][0] = 0;
    
                if(aa[1] == aa[2]) dp[1][1] = 1;
                else dp[1][0] = 1;
    
                for(int i = 2;i <= n + m - 1;++i){
                    dp[i][0] = dp[i][1] = 0;
                    int a = aa[i],b = aa[i+1];
                    int k = min(a,b);
                    bool ys = M[i][k];
                    int ccnt = cnt[i];
                    ll rem = (qpow(k,ccnt) - qpow(k-1,ccnt) + MOD) % MOD;
    
                    // dp[i-1][1]
                    if(b <= a){
                        if(ys) dp[i][1] = (dp[i][1] + dp[i-1][1] * qpow(b,ccnt) % MOD) % MOD;
                        else dp[i][1] = (dp[i][1] + dp[i-1][1] * rem % MOD) % MOD;
                    }
    
    
                    if(b <= a && !ys){
                        dp[i][0] = (dp[i][0] + dp[i-1][1] * qpow(b-1,ccnt) % MOD) % MOD;
                    }else if(a < b){
                        dp[i][0] = (dp[i][0] + dp[i-1][1] * qpow(a,ccnt) % MOD) % MOD;
                    }
    
                    // dp[i-1][0]
                    if(a > b){
                        //no solution
                    }else if(a < b){
                        if(ys)  dp[i][0] = (dp[i][0] + dp[i-1][0] * qpow(a,ccnt) % MOD) % MOD;
                        else dp[i][0] = (dp[i][0] + dp[i-1][0] * rem % MOD) % MOD;
                    }else if(a == b){
                        if(ys)  dp[i][1] = (dp[i][1] + dp[i-1][0] * qpow(a,ccnt) % MOD) % MOD;
                        else dp[i][1] = (dp[i][1] + dp[i-1][0] * rem % MOD) % MOD;
                    }
    //                show(i,dp[i][0],dp[i][1]);
                }
    
                ans = dp[n+m-1][1];
            }
    
            printf("Case #%d: %lld\n",kase,ans);
        }
        return 0;
    }
    /**
    
    */
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
  • 相关阅读:
    网课搜题公众号搭建 查题接口教程
    【flink-sql实战】flink 主键声明与upsert功能实战
    拯救者y9000k(2022版)安装ubuntu系统(解决wifi问题)
    Android ViewPager2 + Fragment 联动
    SQLMAP插件tamper编写与使用
    盛元广通矿企煤炭检测实验室信息管理系统3.0
    Jmeter性能测试工具使用总结
    IO流 - File - properties
    对reduce理解,以及几种常见的应用场景
    【服务器数据恢复】IBM某型号服务器RAID5磁盘阵列数据恢复案例
  • 原文地址:https://blog.csdn.net/weixin_45775438/article/details/127465525