【训练题71:动态规划】Building Blocks | Gym102822B
题意
- Building Blocks | Gym102822B
- 这是一堆方块物体的三视图

- 现在定义正左视图,看到的就是这样的

- 给定 n,mn,mn,m 表示俯视图的矩阵的行和列
给定 kkk 个条件,第 iii 个条件表示俯视图的第 xix_ixi 行第 yiy_iyi 列的位置的高为 hih_ihi
给定正左视图的每个位置的高度 aia_iai。注意到一共有 n+mn+mn+m 个位置
问你所有合法情况的方案数取模 1e9+71e9+71e9+7
当然满足若某个位置有方块,它的底下一定有方块。
还要满足对于俯视图的每个位置,必须至少有一个方块。 - 1≤n,m,k≤1051\le n,m,k\le 10^51≤n,m,k≤105
1≤ai,hi≤1091\le a_i,h_i\le 10^91≤ai,hi≤109
保证每个条件的位置均不同。
思路
- 注意到,正左视图的第 iii 个位置影响俯视图两个斜列的位置。
我们肯定按俯视图一个斜列一个斜列去计算,这样复杂度可以达到 O(n+m)O(n+m)O(n+m)
首先是处理俯视图位置 (i,j)(i,j)(i,j) 是正左视图第几个斜列,容易得到为 i+j−1i+j-1i+j−1,记作 f(i,j)=i+j−1f(i,j)=i+j-1f(i,j)=i+j−1
还要处理一下每个斜列还剩下有多少个位置没有填,记作 cnt[i]cnt[i]cnt[i] - 注意到,我们考虑当前斜列

- 这一斜列用对角线一划,可以看到对应了正左视图的两个高度,记作 a,ba,ba,b
自然需要满足的,就是这个斜列位置的最高高度应该为 min(a,b)\min(a,b)min(a,b)
当然还有,就是对于 aaa 所对应的两个斜列的最大值为 aaa,对 bbb 同理。
可以感觉到,aaa 还被上一斜列所影响。
那么很自然就可以想到一个动态规划
定义 dp[x][0]dp[x][0]dp[x][0] 表示考虑完前 xxx 个斜列,最后一个斜列的最大值还没有取到,或者说还没有满足
dp[x][1]dp[x][1]dp[x][1] 表示考虑完前 xxx 个斜列,最后一个斜列的最大值取到了,或者说满足了 - 首先考虑一些非法的情况
因为有 kkk 个已知条件,这个位置的高度就不能大于它对应正左视图的两个位置的值
即 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] - 接下来考虑递推转移
- 首先,假设上一个状态为 dp[i−1][1]dp[i-1][1]dp[i−1][1],即 aaa 已经满足了
- 如果我们希望 bbb 也满足,即转移到 dp[i][1]dp[i][1]dp[i][1],那么需要 b≤ab\le ab≤a,不然就破坏了正左视图上一位置最大值为 aaa 的条件
- 如果这一斜列有高度 bbb 了,那么剩余 cntcntcnt 个位置,1∼b1\sim b1∼b 随意填,方案数 bcntb^{cnt}bcnt
- 如果这一斜列没有高度 bbb,那么我们需要满足 cnt>0cnt>0cnt>0,这些位置可以填 1∼b1\sim b1∼b,且满足至少有一个 bbb
简单容斥,方案数即为 bcnt−(b−1)cntb^{cnt}-(b-1)^{cnt}bcnt−(b−1)cnt
但是如果 cnt=0cnt=0cnt=0,那么方案数为 000,不影响
- 如果我们希望转移到 dp[i][0]dp[i][0]dp[i][0],即目前的 bbb 暂时不取到
- 如果 b≤ab\le ab≤a 并且这一斜列没有高度 bbb,才是可以的,每个位置 1∼(b−1)1\sim (b-1)1∼(b−1) 随便选
方案数为 (b−1)cnt(b-1)^{cnt}(b−1)cnt - 如果 b>ab>ab>a,那么我们这个位置可以选择的数为 1∼a1\sim a1∼a,注意不能破坏最大值为 aaa 的限制
方案数为 acnta^{cnt}acnt
- 如果上一个状态为 dp[i−1][0]dp[i-1][0]dp[i−1][0],那么我们 aaa 就一定要满足,不然就非法了。
- 如果 a>ba>ba>b,我们最大值要放 aaa 但是又不能超过 bbb,自然无解
- 如果 a
- 如果当前斜列有元素 aaa,那么我们自然满足了条件 aaa,且我们能放的元素为 1∼a1\sim a1∼a,即 bbb 无法目前满足
方案数就是 acnta^{cnt}acnt - 如果当前斜列没有元素 aaa,那么我们能放的元素为 1∼a1\sim a1∼a且至少有一个 aaa
同上方案数为 acnt−(a−1)cnta^{cnt}-(a-1)^{cnt}acnt−(a−1)cnt
自然需要满足 cnt>0cnt>0cnt>0
但是如果 cnt=0cnt=0cnt=0,那么方案数为 000,不影响
- 如果 a=ba=ba=b,那么自然满足了 aaa 也同时满足了 bbb,即转移到 dp[i][1]dp[i][1]dp[i][1]
转移方案和 a
可以看到,我们只要先预处理一下第 iii 斜列是否存在元素 xxx ,记一个 ump 即可
代码
#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;}templatevoid 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_mapM[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){// initdp[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;
}
/***/