给定
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
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
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
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#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;}/**
*/