[20191005机房测试] Silhouette

有一个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;
}
原文地址:https://www.cnblogs.com/tqr06/p/11625473.html