[NOIP模拟10.24][容斥原理]Silhouette

探讨一种算法,用于计算符合特定正视图和左视图的三维立方体堆叠方案数量。通过数学建模和动态规划解决整数解数量问题,实现高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

有一个n n的网格,在每个格子上堆叠了一些边长为1的立方体。 现在给出这个三维几何体的正视图和左视图,求有多少种与之符合的堆叠立方体的方
案。两种方案被认为是不同的,当且仅当某个格子上立方体的数量不同。 输出答案对109 + 7取模的结果。

Sample Input

2
1 2
2 1

Sample Output

5

题解

容易发现,其实就是要求这样一个方程的整数解数量
∀i∈[1,n],max(xj,i)=Aj,max(xj,i)=Bi\forall i\in[1,n] ,max(x_{j,i})=A_j,max(x_{j,i})=B_ii[1,n],max(xj,i)=Aj,max(xj,i)=Bi
A,BA,BA,B排序,显然这样对答案没有影响。判断最大值是否相等即可判断无解
我们考虑枚举A,BA,BA,B中所有数SSS,每次确定最大值是SSS的这片区域的方案数
先考虑总最大值为SSS的情况,显然这样的区域是一个a∗ba*bab的矩形
f[i]f[i]f[i]表示至少有iii行不合法的方案数(保证列全部合法)
有转移
f[i]=∑Cai∗(Si∗((S+1)a−i−Sa−i))bf[i]=\sum C_{a}^{i}*(S^i*((S+1)^{a-i}-S^{a-i}))^bf[i]=Cai(Si((S+1)aiSai))b
.简单容斥可知答案
f[0]=∑−1kf[k]f[0]=\sum-1^kf[k]f[0]=1kf[k]
再考虑SSS不是最大值的情况
画图可知这样的区域可能是LLL形或者矩形
我们发现, LLL形可能产生非法情况的只有在中间交界处的那个矩形
因为上面的行更大,不可能非法。右边的列更大,也不可能非法
设这个矩形是a∗ba*bab的,上面还有ccc行,右边还有ddd
有转移
f[i]=∑Cai∗(Si∗((S+1)a+c−i−Sa+c−i))b∗(Si∗((S+1)a−i−Sa−i))df[i]=\sum C_{a}^i*(S^i*((S+1)^{a+c-i}-S^{a+c-i}))^b*(S^i*((S+1)^{a-i}-S^{a-i}))^df[i]=Cai(Si((S+1)a+ciSa+ci))b(Si((S+1)aiSai))d
左边处理的是一个(a+c)∗b(a+c)*b(a+c)b的矩阵,右边处理的是一个a∗da*dad的矩阵
同样容斥f[0]=∑−1kf[k]f[0]=\sum-1^kf[k]f[0]=1kf[k]可以知道答案
复杂度nlognnlognnlogn

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define mod 1000000007
using namespace std;
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline void write(int x)
{
	if(x<0)putchar('-'),x=-x;
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
inline void pr1(int x){write(x);printf(" ");}
inline void pr2(int x){write(x);puts("");}
LL pow_mod(LL a,LL b)
{
	LL ret=1;
	while(b)
	{
		if(b&1)ret=ret*a%mod;
		a=a*a%mod;b>>=1;
	}
	return ret;
}
LL inv[110000],pre[110000];
LL C(int n,int m){return pre[n]*inv[m]%mod*inv[n-m]%mod;}
LL solve(LL a,LL b,LL c,LL d,LL s)
{
	LL ret=0;
	for(int i=0;i<=a;i++)
	{
		LL sum=C(a,i)*pow_mod(pow_mod(s,i)*((pow_mod(s+1,a+c-i)-pow_mod(s,a+c-i)+mod)%mod)%mod,b)%mod;
		sum=sum*pow_mod(pow_mod(s,i)*pow_mod(s+1,a-i)%mod,d)%mod;
		if(i&1)ret=(ret-sum+mod)%mod;
		else ret=(ret+sum)%mod;
	}
	return ret;
}
int s1[110000],s2[110000],tt[210000],ln;
int n;
int main()
{
	pre[0]=1;for(int i=1;i<=100000;i++)pre[i]=pre[i-1]*i%mod;
	inv[100000]=pow_mod(pre[100000],mod-2);
	for(int i=99999;i>=0;i--)inv[i]=inv[i+1]*(i+1)%mod;
	n=read();
	for(int i=1;i<=n;i++)s1[i]=read(),tt[++ln]=s1[i];
	for(int i=1;i<=n;i++)s2[i]=read(),tt[++ln]=s2[i];
	sort(s1+1,s1+1+n);sort(s2+1,s2+1+n);
	sort(tt+1,tt+1+ln);ln=unique(tt+1,tt+1+ln)-(tt+1);
	if(s1[n]!=s2[n])return puts("0"),0;
	int lst1=n+1,lst2=n+1,u1=n,u2=n;
	LL ans=1;
	for(int i=ln;i>=1;i--)
	{
		while(s1[u1-1]==tt[i]&&u1>1)u1--;
		while(s2[u2-1]==tt[i]&&u2>1)u2--;
		ans=(ans*solve(lst1-u1,lst2-u2,n-lst1+1,n-lst2+1,tt[i])%mod);
		lst1=u1;lst2=u2;
	}
	pr2(ans);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值