给定 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 1≤n,m,k≤105 1 ≤ a i , h i ≤ 1 0 9 1\le a_i,h_i\le 10^9 1≤ai,hi≤109 保证每个条件的位置均不同。
思路
注意到,正左视图的第 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+j−1,记作 f ( i , j ) = i + j − 1 f(i,j)=i+j-1 f(i,j)=i+j−1 还要处理一下每个斜列还剩下有多少个位置没有填,记作 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[i−1][1],即 a a a 已经满足了
如果我们希望 b b b 也满足,即转移到 d p [ i ] [ 1 ] dp[i][1] dp[i][1],那么需要 b ≤ a b\le a b≤a,不然就破坏了正左视图上一位置最大值为 a a a 的条件
如果这一斜列有高度 b b b 了,那么剩余 c n t cnt cnt 个位置, 1 ∼ b 1\sim b 1∼b 随意填,方案数 b c n t b^{cnt} bcnt
如果这一斜列没有高度 b b b,那么我们需要满足 c n t > 0 cnt>0 cnt>0,这些位置可以填 1 ∼ b 1\sim b 1∼b,且满足至少有一个 b b b 简单容斥,方案数即为 b c n t − ( b − 1 ) c n t b^{cnt}-(b-1)^{cnt} bcnt−(b−1)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 b≤a 并且这一斜列没有高度 b b b,才是可以的,每个位置 1 ∼ ( b − 1 ) 1\sim (b-1) 1∼(b−1) 随便选 方案数为 ( b − 1 ) c n t (b-1)^{cnt} (b−1)cnt
如果 b > a b>a b>a,那么我们这个位置可以选择的数为 1 ∼ a 1\sim a 1∼a,注意不能破坏最大值为 a a a 的限制 方案数为 a c n t a^{cnt} acnt
如果上一个状态为 d p [ i − 1 ] [ 0 ] dp[i-1][0] dp[i−1][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 1∼a,即 b b b 无法目前满足 方案数就是 a c n t a^{cnt} acnt
如果当前斜列没有元素 a a a,那么我们能放的元素为 1 ∼ a 1\sim a 1∼a且至少有一个 a a a 同上方案数为 a c n t − ( a − 1 ) c n t a^{cnt}-(a-1)^{cnt} acnt−(a−1)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>#defineIOSios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;typedeflonglong ll;voidshow(){
std::cerr << endl;}template<typename T,typename... Args>voidshow(T x,Args... args){
std::cerr <<"[ "<< x <<" ] , ";show(args...);}constint MAX =2e5+50;constint MOD =1e9+7;constint INF =0x3f3f3f3f;const ll LINF =0x3f3f3f3f3f3f3f3f;constdouble 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)return0;}return res;}
ll inv(ll a){
/* */returnqpow(a,MOD-2);}
ll inv(ll a,ll p){
returnqpow(a,p-2,p);}
unordered_map<int,bool>M[MAX];int aa[MAX];int cnt[MAX];
ll dp[MAX][2];intF(int n,int m){
return n + m -1;}intmain(){
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;}elseif(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}elseif(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;}elseif(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);}return0;}/**
*/