2020 China Collegiate Programming Contest - Mianyang Site 2020 CCPC 绵阳站 B. Building Blocks(dp)

传送门
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

题意

T T T组数据,每组数据给你三个正整数 n , m , k n,m,k n,m,k,其中 n , m n,m nm分别为积木的(积木由若干个 1 × 1 × 1 1\times1\times1 1×1×1的小方块组成),再给你左前视图(如图所示)每一部分的最终高度 a i a_i ai(共 n + m n+m n+m部分),接下来 k k k行,每行三个正整数 x , y , h x,y,h x,y,h,表示第 x x x y y y列的高度指定为正整数 h h h,问你合法的积木总数,对 1 0 9 + 7 10^9+7 109+7取模。
在这里插入图片描述

数据范围:

1 ⩽ T , n , m , k ⩽ 1 0 5 , 1 ⩽ a i , h i ⩽ 1 0 9 1\leqslant T,n,m,k\leqslant 10^5,1\leqslant a_i,h_i\leqslant 10^9 1T,n,m,k105,1ai,hi109
1 ⩽ x i ⩽ n , 1 ⩽ y i ⩽ m , ∑ n , ∑ m , ∑ k ⩽ 1 0 5 1\leqslant x_i\leqslant n,1\leqslant y_i\leqslant m,\sum_{}n,\sum_{} m,\sum_{} k\leqslant 10^5 1xin,1yim,n,m,k105

思路

①我们设 l i m i , c n t i , p i lim_i,cnt_i,p_i limi,cnti,pi分别为左前视图第 i i i部分的 高度(即 a i a_i ai)、还剩多少个没指定高度的方块、通过已经指定的方块达到的高度。
注意:左前视图第 i i i部分的高度只会受到指定方块 ( x , y ) (x,y) (x,y) x + y = i 或 者 x + y = i + 1 x+y=i或者x+y=i+1 x+y=ix+y=i+1)的高度的影响。
显然有两种不合法的情况(即答案为0): c n t i < 0 cnt_i<0 cnti<0或者 p i > l i m i p_i>lim_i pi>limi
③设 d p [ i ] [ 0 / 1 ] dp[i][0/1] dp[i][0/1]分别表示左前视图第 i i i部分的高度 没有/有 达到最终高度的合法的方案总数。
设函数 c a l ( x , n ) cal(x,n) cal(x,n)表示 n n n个位置每个位置有 1 , 2 , . . . , x 1,2,...,x 1,2,...,x x x x种高度可以选择,并且高度 x x x一定被选择的方案总数。
设函数 p o w e r ( x , n ) power(x,n) power(x,n)表示 n n n个位置每个位置有 1 , 2 , . . . , x 1,2,...,x 1,2,...,x x x x种高度可以选择的方案总数。
因此可以分两种情况转移(为了表述更清晰,以下状态转移的时候忽略取模操作):
a = l i m i − 1 , b = l i m i a=lim_{i-1},b=lim_i a=limi1,b=limi
1 、 p i < l i m i 1、p_i<lim_i 1pi<limi
d p [ i ] [ 1 ] + = d p [ i − 1 ] [ 1 ] × c a l ( b , c n t i ) , ( a ⩾ b ) dp[i][1]+=dp[i-1][1]\times cal(b,cnt_i),(a \geqslant b) dp[i][1]+=dp[i1][1]×cal(b,cnti),(ab)
d p [ i ] [ 1 ] + = d p [ i − 1 ] [ 0 ] × c a l ( a , c n t i ) , ( a = = b ) dp[i][1]+=dp[i-1][0]\times cal(a,cnt_i),(a==b) dp[i][1]+=dp[i1][0]×cal(a,cnti),(a==b)
d p [ i ] [ 0 ] + = d p [ i − 1 ] [ 1 ] × p o w e r ( m i n ( a , b − 1 ) , c n t i ) dp[i][0]+=dp[i-1][1]\times power(min(a,b-1),cnt_i) dp[i][0]+=dp[i1][1]×power(min(a,b1),cnti)
d p [ i ] [ 0 ] + = d p [ i − 1 ] [ 0 ] × c a l ( a , c n t i ) , ( a < b ) dp[i][0]+=dp[i-1][0]\times cal(a,cnt_i),(a<b) dp[i][0]+=dp[i1][0]×cal(a,cnti),(a<b)
2 、 p i = = l i m i 2、p_i==lim_i 2pi==limi
d p [ i ] [ 1 ] + = d p [ i − 1 ] [ 1 ] × p o w e r ( m i n ( a , b ) , c n t i ) dp[i][1]+=dp[i-1][1]\times power(min(a,b),cnt_i) dp[i][1]+=dp[i1][1]×power(min(a,b),cnti) (这个式子一定要仔细思考)
d p [ i ] [ 1 ] + = d p [ i − 1 ] [ 0 ] × c a l ( a , c n t i ) , ( a ⩽ b ) dp[i][1]+=dp[i-1][0]\times cal(a,cnt_i),(a\leqslant b) dp[i][1]+=dp[i1][0]×cal(a,cnti),(ab)
d p [ i ] [ 0 ] = 0 dp[i][0]=0 dp[i][0]=0
④时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn),注意初始化和取模。
PS:看似只有简单的几个转移方程,实际上要每种情况都思考的非常清楚,不能有任何偏差,这正是这道题目的难度所在。

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dep(i,a,b) for(int i=(a);i>=(b);i--)
#define VV vector<int>
#define PP pair<int,int>
#define ls (rt<<1)
#define rs (rt<<1|1)
#define fi first
#define se second
#define pb push_back
using namespace std;
typedef long long ll;
template <typename T>
inline void read(T &X){
    X=0;int w=0; char ch=0;
    while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    if(w) X=-X;
}
const int maxn=4e5+5;
//const double pi=acos(-1.0);
//const double eps=1e-9;
const ll mo=1e9+7;
ll n,m,k;
ll a[maxn],c[maxn];
ll ans;
ll p[maxn], lim[maxn], cnt[maxn];
ll dp[maxn][2];
ll power(ll a,ll n){
    ll sum = 1;
    while(n){
        if(n&1)
            sum = sum * a % mo;
        n >>= 1;
        a = a * a % mo;
    }
    return sum;
}
ll cal(ll a, ll n) { return (power(a, n) - power(a - 1, n) + mo) % mo; }
void solve(){
    read(n);read(m);read(k);
    int len = n + m;
    rep(i, 0, len) p[i] = lim[i] = cnt[i] = 0;
    rep(i,1,len){
        read(lim[i]);
        cnt[i + 1] = min(min(n, m), min(1LL * i, n + m - i));
    }
    rep(i,1,k){
        int x, y, h;
        read(x);
        read(y);
        read(h);
        p[x + y] = max(p[x + y], 1LL * h);
        p[x + y - 1] = max(p[x + y - 1], 1LL * h);
        cnt[x + y]--;
    }
    rep(i,1,len) if(cnt[i]<0||p[i]>lim[i]){
        puts("0");
        return;
    }
    dp[1][1] = 1;
    dp[1][0] = 1;
    if(p[1]==lim[1])
        dp[1][0] = 0;
    else
        dp[1][1] = 0;//
    rep(i,2,len){
        dp[i][0] = dp[i][1] = 0;
        ll a = lim[i - 1], b = lim[i];
        if(p[i]<lim[i]){
            if(a>=b)//?
                dp[i][1] = (dp[i][1] + dp[i - 1][1] * cal(b, cnt[i]) % mo) % mo;//
            if(a==b)//
                dp[i][1] = (dp[i][1] + dp[i - 1][0] * cal(a, cnt[i]) % mo) % mo;//
            if(a<b)//
                dp[i][0] = (dp[i][0] + dp[i - 1][0] * cal(a, cnt[i]) % mo) % mo;//
            dp[i][0] = (dp[i][0] + dp[i - 1][1] * power(min(a, b - 1), cnt[i]) % mo) % mo;
        }
        else {
            dp[i][1] = (dp[i][1] + dp[i - 1][1] * power(min(a, b), cnt[i]) % mo) % mo;//?
            if(a<=b)//
                dp[i][1] = (dp[i][1] + dp[i - 1][0] * cal(a, cnt[i]) % mo) % mo;//
            dp[i][0] = 0;
        }
    }
    printf("%lld\n", dp[len][1]);
}
int main(){
    //freopen("e://duipai//data.txt","r",stdin);
    //freopen("e://duipai//amyout.txt","w",stdout);
    int T=1,cas=1;
    read(T);
    while(T--){
        printf("Case #%d: ", cas++);
        solve();
    }
    //system("pause");
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值