有一个$n\times n$的网格,在每一个格子上堆叠了一些边长为$1$的立方体。
如今给出这个三维几何体的正视图和左视图,求有多少种与之符合的堆叠立方体的方案。两种方案被认为是不一样的,当且仅当某个格子上立方体的数量不一样。
输出答案对$10^9+7$取模的结果。c++
从文件$silhouette.in$中读入数据。
第一行一个整数$n$。
第二行$n$个整数,第$i$个表示正视图中从左到右第$i$个位置的高度$A_i$。
第三行$n$个整数,第$i$个表示左视图中从左到右第$i$个位置的高度$B_i$。spa
输出到文件$silhouette.out$中。
输出一行表示答案。blog
样例输入1:排序
2
1 2
2 1get
样例输出1:it
5class
样例输入2:im
3
3 1 3
2 3 2d3
样例输出2:统计
175
样例输入3:
3
1 1 1
3 3 3
样例输出3:
0
样例$1$解释:
正视图和左视图:
_ _
_|_| |_|_
|_|_| |_|_|
若是用$2\times 2$的矩阵来表示每一个格子上堆叠的立方体个数,则五种方案能够表示为:
1 2 0 2 1 2 0 2 1 2
1 1 1 0 1 0 1 1 0 1
数据范围:
对于全部数据,有$1\leqslant n\leqslant 10^5,1\leqslant A_i,B_i\leqslant 10^9$。
$\bullet Subtask1(3\%)$,$n=1$。
$\bullet Subtask2(14\%)$,$n\leqslant 3,A_i,B_i\leqslant 4$。
$\bullet Subtask3(17\%)$,$n\leqslant 16$。
$\bullet Subtask4(24\%)$,$n\leqslant 100$。
$\bullet SUbtask5(13\%)$,$n\leqslant 3,000$。
$\bullet Subtask6(18\%)$,$A_i,B_i$分别构成了一个$1$至$n$的排列。
$\bullet Subtask7(11\%)$,没有特殊的约束。
显然对于这道题,咱们要求如下方程的解的个数:
$$\forall i\in [1,n],\max \limits{j=1}^n x_{i,j}=A_i,\max \limits_{j=1}^n x_{j,i}=B_i$$
咱们能够现将$A,B$排序,由于这样对结果没有影响,简单证实一下:
先来考虑列,对于每个$A_i$,不管哪一列在前,哪一列在后,最终全部列在第$i$行的最大值都须要是$A_i$;行同理。
若是最大的$A_i$不等于最大的$B_i$,那么无解,不然必定有解,再也不证实(主要是没有人问过)。
而后咱们从大到小枚举$A,B$中每个值$S$,对于排好序后每个$S$,它们会是这样的:
相似分红了一层一层的。
而后咱们枚举每一层。
先说$S_1$这一层,对于$S_1$有一个特殊性质,$S_1$是全部$S$中最大的,先设$S_1$所涉及到的这个矩形的长宽分别为$a,b$,以下图:
咱们在设$f[i]$表示$a$行中,至少有$i$行必定不合法的方案数,这里说的不合法是指高度没有到达$S$,也就是说不能对投影作贡献;之因此咱们要设为至少,是由于这样每两行之间就能够不互相影响了。
给出状态转移方程:
$$f[i]=C_a^i\times (S^i\times ((S+1)^{a-i}-S^{a-i}))^b$$
来解释一下状态转移方程:
首先,组合数必不可少,由于咱们要从$a$行里选出$i$行;再解释后面的$b$次方,由于有$b$列,这$b$列互不影响(前面有解释);再来解释$S^i$,由于有$i$个位置必定不合法,这$i$个位置能够选$0\sim S-1$这$S$个高度;最后来理解$(S+1)^{a-i}-S^{a-i}$,由于还剩下$a-i$个位置,每个位置选多高均可以,因此有$(S+1)^{a-i}$,可是咱们要保证这一列必定合法,因此还要减去$S^{a-i}$。
由于咱们$f[i]$的定义为至少有$i$行不合法的方案数,因此咱们还须要求得刚好有$i$行不合法的方案数,而咱们只须要知道刚好有$0$行不合法的方案数(设为$res$),也就是都合法的方案数,考虑容斥,则有:
$$res=\sum \limits_{i=0}^a(-1)^i\times f[i]$$
那么,咱们在来考虑通常状况,也就是上图中$S_2$之后的全部$S$,由于每当咱们处理完一个$S$以后,下一个区域的形状只有$L$行或矩形,以下图(红色为上一次处理的区域,紫色为这一次处理的区域):
而不管对于哪一种状况,咱们都按以下划分方式将其划分为两部分:
对于矩形的两种状况,无非就是没有了那一部分,能够认为举行是一种特殊的$L$行。
为方便,咱们不妨将$L$行区域按以下图方式标号$a,b,c,d$:
那么,如今我给出状态转移方程:
$$f[i]=C_a^i\times (S^i\times ((S+1)^{a+c-i}-S^{a+c-i}))^b\times (S^i\times (S+1)^{a-i})^d$$
如今来解释这个更复杂的式子,先明确一下,由于上图中红色区域已经处理完毕,因此对于蓝色区域,其已经知足行,而没有知足列,对于绿色区域同理。
先来解释式子中的组合数$C_a^i$,你可能会存在疑问,为何有$a+c$行,而不是$C_{a+c}^i$;那是由于上面我也明确过了,对于$c$行,行已经知足,咱们不能让它不知足,而这$i$行的不知足咱们只能从$a$行出;因此是$C_a^i$,而不是$C_{a+c}^i$。
再来解释$(S^i\times ((S+1)^{a+c-i}-S^{a+c-i}))^b$,对于$b$次方和$S^i$,上面对于$S$是最大的值的状况已经作了解释,在此就不作过多的赘述;直接解释$(S+1)^{a+c-i}-S^{a+c-i}$,这个也很好理解,与上面状况相似,有$i$行不合法,那么还有$a+c-i$个位置能够合法,而咱们要保证这一列必定合法,因此还要减去不合法的状况。
最后来看后面新填进来的这一部分,应该能看的出来,这部分是为了处理矩形$a\times d$的,也就是区域$2$,式子与前面大致相似,再也不赘述,只来思考这样一个问题:网上有另外一篇题解(在我写以前可能也只有那一篇,而我就是按照那一篇的思路调出来的),他的后面这部分是$S^i\times (S+1)^{a-i}-S^{a-i})^d$,可是个人式子里却没有$-S^{a-i}$,他的式子的漏洞就在这里,而为何不用这部分呢?由于咱们减去$S^{a-i}$是为了让那一列合法,仍是开始我明确的那个问题,由于咱们在处理红色区域的时候已经保证了其合法,而红色区域的$S$要比矩形$a\times d$(绿色区域)的大,也就是高,因此不管如何其正视图都已经不变化了,变化的只有左视图,而咱们须要作的就是让左视图合法,不用考虑列合不合法的问题,因此不用减去$S^{a-i}$。
对于统计合法方案数的容斥,其式子亦是:
$$res=\sum \limits_{i=0}^a (-1)^i\times f[i]$$
对于$c=0$和$d=0$的状况,咱们将其带入上式会发现对结果并没有影响,因而咱们能够将特殊状况的式子和普通状况的式子合并成一个。
这样咱们从达到小不断枚举每个$S$便可求出全部合法方案数。
时间复杂度:$\Theta(n\log n)$。
指望得分:$100$分。
实际得分:$100$分。
#include<bits/stdc++.h>
using namespace std;
const int mod=1000000007;
int n;
int A[100001],B[100001],S[200001];
long long jc[100001],inv[100001];
long long ans=1;
long long qpow(long long x,long long y)
{
long long res=1;
while(y)
{
if(y&1)res=res*x%mod;
x=x*x%mod;
y>>=1;
}
return res;
}
void pre_work()
{
jc[0]=1;
for(long long i=1;i<=100000;i++)jc[i]=jc[i-1]*i%mod;
inv[100000]=qpow(jc[100000],mod-2);
for(long long i=100000;i>0;i--)inv[i-1]=inv[i]*i%mod;
}
long long get_C(int x,int y){return jc[x]*inv[y]%mod*inv[x-y]%mod;}
long long lucas(int x,int y)
{
if(!y)return 1;
return get_C(x%mod,y%mod)*lucas(x/mod,y/mod)%mod;
}
long long solve(int a,int b,int c,int d,int s)
{
long long res=0;
for(int i=0;i<=a;i++)
{
long long now=lucas(a,i)*qpow(qpow(s,i)*((qpow(s+1,a+c-i)-qpow(s,a+c-i)+mod)%mod)%mod,b)%mod*qpow(qpow(s,i)*qpow(s+1,a-i)%mod,d)%mod;
if(i&1)res=(res-now+mod)%mod;
else res=(res+now)%mod;
}
return res;
}
int main()
{
pre_work();
scanf("%d",&n);
for(int i=1;i<=n;i++){scanf("%d",&A[i]);S[i]=A[i];}
for(int i=1;i<=n;i++){scanf("%d",&B[i]);S[n+i]=B[i];}
sort(A+1,A+n+1);
sort(B+1,B+n+1);
if(A[n]!=B[n]){puts("0");return 0;}
sort(S+1,S+2*n+1);
S[0]=unique(S+1,S+2*n+1)-S-1;
int prea=n+1,preb=n+1,nowa=n,nowb=n;
for(int i=S[0];i;i--)
{
while(A[nowa-1]==S[i]&&nowa-1)nowa--;
while(B[nowb-1]==S[i]&&nowb-1)nowb--;
ans=ans*solve(prea-nowa,preb-nowb,n-prea+1,n-preb+1,S[i])%mod;
prea=nowa;preb=nowb;
}
printf("%lld",ans);
return 0;
}
rp++