[CSP-S模擬測試]:Silhouette(數學)

題目描述

  有一個$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 1
get

樣例輸出1:it

5class

樣例輸入2:im

3
3 1 3
2 3 2
d3

樣例輸出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++

相關文章
相關標籤/搜索