有一个n × n的网格,在每个格子上堆叠了一些边长为1的立方体
现在给出这个三维几何体的正视图和左视图,求有多少种与之符合的堆叠立方体的方案
两种方案被认为是不同的,当且仅当某个格子上立方体的数量不同
输出答案对1e9 + 7取模的结果。
给出的是这一坨正方体们的正视图和左视图
开始想的是先求出每一个位置可能的数量范围
求出全集,然后减去每一行/列不符合的拼法
然而这样减了会减多,于是再加上多减的……这就是容斥了
时间复杂度巨大,根本没有过的可能
于是我们考虑确定从某一个方向看过去的视角符合题意
此时我们再容斥即可
容斥的式子不想推了,还得写总结呢
不过题解的方法是真的秀,比我之前的方法快一倍……
代码:
#include<bits/stdc++.h>
#define N 100005
#define ll long long
#define mod 1000000007
using namespace std;
ll xx1,xx2,yy1,yy2;
ll n,ans=1,a[N],b[N],c[N<<1],m;
ll qpow(ll n,ll k)
{
ll res=1;
while(k)
{
if(k&1) res=(res*n)%mod;
n=(n*n)%mod;
k>>=1;
}
return res;
}
ll fac[N],invfac[N];
inline void init()
{
fac[0]=1;
for(register int i=1;i<=N-3;++i) fac[i]=(fac[i-1]*i)%mod;
invfac[N-4]=qpow(fac[N-4],mod-2);
for(register int i=N-5;i>=0;--i) invfac[i]=((i+1)*invfac[i+1])%mod;
sort(a+1,a+n+1);
sort(b+1,b+n+1);
sort(c+1,c+m+1);
}
ll C(ll n,ll m) {return ((fac[n]*invfac[m])%mod*invfac[n-m])%mod;}
template<class T>inline void read(T &res)
{
char c;T flag=1;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
}
ll cal(ll a,ll b,ll c,ll d,ll s)
{
ll res=0;
for(register ll i=0;i<=a;++i)
{
ll sum=C(a,i)*qpow(qpow(s,i)*((qpow(s+1,a+c-i)-qpow(s,a+c-i)+mod)%mod)%mod,b)%mod;
sum=sum*qpow(qpow(s,i)*qpow(s+1,a-i)%mod,d)%mod;
if(i&1) res=(res-sum+mod)%mod;
else res=(res+sum)%mod;
}
return res;
}
int main()
{
freopen("silhouette.in","r",stdin);
freopen("silhouette.out","w",stdout);
read(n);
for(register int i=1;i<=n;++i)
{
read(a[i]);
c[++m]=a[i];
}
for(register int i=1;i<=n;++i)
{
read(b[i]);
c[++m]=b[i];
}
init();
if(a[n]!=b[n]) {puts("0");return 0;}
m=unique(c+1,c+m+1)-(c+1);
xx1=xx2=n+1;
yy1=yy2=n;
for(int i=m;i>=1;i--)
{
while(a[yy1-1]==c[i]&&yy1>1) yy1--;
while(b[yy2-1]==c[i]&&yy2>1) yy2--;
ans=(ans*cal(xx1-yy1,xx2-yy2,n-xx1+1,n-xx2+1,c[i])%mod);
xx1=yy1;
xx2=yy2;
}
printf("%lld
",ans);
return 0;
}