【训练题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 a<b a<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 a<b a<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 <bits/stdc++.h>
#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;
}
/**

*/

猜你喜欢

转载自blog.csdn.net/weixin_45775438/article/details/127465525