NOI模擬賽 T1.permutation 排列 (計數)

題意

定義一種長度爲𝑛的排列,滿足任意兩個相鄰的數的和都≤ 𝑚,現在要求你
計算它的個數,記爲𝑆(𝑛, 𝑚)。

但是出題人覺得這道題不足以讓人提起興趣去做,於是稍作修改,給定𝑛, 𝑘
要求你計算:
i=1nS(i,i+k)\sum_{i=1}^nS(i,i+k)

答案對 2000002320000023 取模。

題解

顯然當nkn\le k時和式的每一項都是i!i!,答案就是階乘和。但n,kn,k範圍都是1e181e18,怎麼算階乘呢?可以發現,當xmodx\ge mod時,x!0x!\equiv0。那麼大於modmod的都不用計算,直接預處理modmod內的就行了。

考慮i>ki>k怎麼計算。令j=i+kj=i+k相當於考慮S(i,j)S(i,j)i<j<2ii<j<2i時如何計算。

對於[1,ji][1,j-i]範圍內的數,它們的排列順序並不影響答案,因爲其中最大的jij-i加上ii也小於等於jj。因此他們內部的偶愛列順序方案就是(ji)!(j-i)!

再考慮[ji+1,i][j-i+1,i]的數放進來。我們的順序是先放ji+1j-i+1,再放ii,再放ji+2j-i+2,再放i1i-1。也就是從兩頭一邊輪流放一個。

那麼放ji+1j-i+1的時候有ji+1j-i+1種方案(已經有jij-i個數,插一個數進去),然後放ii時本該有ji+2j-i+2種方案,但不能放在ji+1j-i+1兩邊,所以要減22,結果就是jij-i種方案。

那麼這樣插入了兩個數,下一次放ji+2j-i+2時由於不能和ii放在一起,所以減22,方案數也是ji+1j-i+1;同樣i1i-1的方案也是jij-i

所以總方案就是(ji)!(j-i)!和很多個(ji+1)(j-i+1)和很多個(j-i)乘起來。具體答案是:
S(i,j)=(ji)!(ji+1)2ij2(ji)2ij2S(i,j)=(j-i)!(j-i+1)^{\lceil\frac{2i-j}2\rceil}(j-i)^{\lfloor\frac{2i-j}2\rfloor}

也就是
S(i,i+k)=k!(k+1)ik2kik2S(i,i+k)=k!(k+1)^{\lceil\frac{i-k}2\rceil}k^{\lfloor\frac{i-k}2\rfloor}

那麼要求和,發現kk定,分奇偶性可以變成兩個等比數列求和。然後就完事了。

CODE

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 20000025;
const int mod = 20000023;
int fac[MAXN], sum[MAXN], ans;
LL n, k;
inline int qpow(int a, int b) {
	int re = 1;
	while(b) {
		if(b&1) re = 1ll * re * a % mod;
		a = 1ll * a * a % mod; b >>= 1;
	}
	return re;
}
int main () {
	freopen("permutation.in", "r", stdin);
	freopen("permutation.out", "w", stdout);
	fac[0] = 1; sum[0] = 0;
	for(int i = 1; i <= mod; ++i) fac[i] = 1ll * fac[i-1] * i % mod, sum[i] = (sum[i-1] + fac[i]) % mod;
	int T; scanf("%d", &T);
	while(T--) {
		scanf("%lld%lld", &n, &k);
		int ans = sum[min(min(n, k), (LL)mod)]; //前面一段用於處理好的階乘
		int s = 0, w = 1ll*k*(k+1)%mod, inv = qpow(w-1+mod, mod-2) % mod; //inv是等比數列求和公式的分母
		if(n > k && k < mod) //k>=mod時因爲後面要乘k的階乘,而k的階乘肯定模出來是0,就沒必要算
			s = (s + 1ll*(k+1)*(qpow(w, ((n-k-1)/2+1)%(mod-1))-1)%mod*inv) % mod; //這個等比數列是從w^0+w^1+...+w^(n-k-1)/2,-1是等比數列求和公式中需要的.
		if(n > k+1 && k < mod)
			s = (s + 1ll*(qpow(w, ((n-k)/2+1)%(mod-1))-1)*inv - 1) % mod; //這個等比數列是從w^1+w^2+...+w^(n-k)/2,所以除去等比公式中的一個-1,後面多減了-1.
		ans = (ans + 1ll * s * fac[min(k,(LL)mod)]) % mod;
		printf("%d\n", ans);
	}
}
發佈了373 篇原創文章 · 獲贊 241 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章