2018-2019 ICPC, NEERC, Northern Eurasia Finals 部分題解

II
感覺這題非常厲害
爲了方便解釋,做以下約定
1.1.將全序列p1,p2...pnp_1,p_2...p_n記爲AA
2.2.AA的某個嚴格子區間記作BiB_i

題目把 interval 定義爲值域大小和區間大小相等的BiB_i
我們定義maximal interval爲不被任意一個interval完整包含的interval

有一個重要結論:如果兩個maximal interval Bx,ByB_x,B_y相交,那麼區間BxByB_x∪B_y一定符合interval的定義,又因爲Bx,ByB_x,B_y是maximal interval,所以若他們相交,那麼並一定就是AA

考慮算出nn的全排列裏所有不合法的排列
依據這個結論,我們可以將不合法的排列分爲兩類
類型11:存在兩個maximal interval Bu,BvB_u,B_v,使BuBv=AB_u∪B_v=A
類型22:A爲大於等於3個maximal interval挨着連在一起組成

我們先考慮類型11,它可以分爲BuB_uBvB_v相交或相鄰兩種情況
BuB_uBvB_v相交,記
Bw=BuBv,Bu=BuBw,Bv=Bv+BwB_w=B_u∩B_v,B_{u'}=B_u-B_w,B_{v'}=B_v+B_w
轉化完後得到的BuBvB_{u'}和B_{v'}是相鄰的,因此我們只要考慮Bu,BvB_u,B_v相鄰的情況

顯然可以發現Bu,BvB_u,B_v的值域分別爲[1,k],[k+1,n][1,k],[k+1,n](證明可以用反證)
不妨假設BuB_uBvB_v的左側(右側的情況方案數是相同的)

爲了避免重複計數,我們強制令BuB_u的prefix中除了自己外,沒有任何一個prefix滿足值域爲[1,k][1,k]且爲interval (若存在這樣的prefix,可以通過上面那個相交變相鄰的轉化轉爲不存在)

那麼就可以統計數量了,記fnf_nnn的所有排列AA中滿足 任何一個prefix都不滿足值域爲[1,k][1,k]且爲interval 的排列數量
fn=n!i=1n1(fi(ni)!)f_n=n!-\sum_{i=1}^{n-1}(f_i*(n-i)!)

接着考慮類型22
對於某個maximal interval BuB_u,他內部是任意排的,我們唯一要滿足的限制是不存在某一段連續的maximal interval可以拼成一個interval
這個限制相當於這些maximal interval的排列是interval-free的
由於我們統計的是非法方案,因此至少存在一個maximal interval BvB_v的大小是>=2的,於是這些maximal interval的個數<n,就化成了子問題
爲了計算這種的方案數,需要一個輔助的dp

gn,kg_{n,k}爲將nn劃分成kk段,每段內部任意排列的方案數
gn,k=i=k1n1gi,k1(ni)!g_{n,k}=\sum_{i=k-1}^{n-1}g_{i,k-1}*(n-i)!

然後就可以算答案了,記ansnans_nnn的所有排列AA中interval-free的排列數
ansn=n!2i=1n1fi(ni)!k=3n1gn,kanskans_n=n!-2\sum_{i=1}^{n-1}f_i*(n-i)!-\sum_{k=3}^{n-1}g_{n,k}*ans_k

maya我的語言表達太差了,寫的題解長度可能是原題解的2倍…

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 405;

int n,mod;
inline void add(int &a,const int &b){ a+=b;if(a>=mod)a-=mod; }
inline void dec(int &a,const int &b){ a-=b;if(a<0) a+=mod; }

int s[maxn];
int f[maxn],ans[maxn];
int g[maxn][maxn];

int main()
{
	//freopen("tmp.in","r",stdin);
	//freopen("tmp.out","w",stdout);
	
	int Tcase; scanf("%d%d",&Tcase,&mod);
	
	s[0]=1; for(int i=1;i<maxn;i++) s[i]=(ll)s[i-1]*i%mod;
	f[1]=1;
	for(int i=2;i<maxn;i++)
	{
		f[i]=s[i];
		for(int j=1;j<i;j++) dec(f[i],(ll)f[j]*s[i-j]%mod);
	}
	g[0][0]=1;
	for(int i=1;i<maxn;i++) for(int j=1;j<=i;j++)
	{
		for(int k=j-1;k<i;k++) add(g[i][j],(ll)g[k][j-1]*s[i-k]%mod);
	}
	
	ans[1]=1;
	for(int i=2;i<maxn;i++)
	{
		ans[i]=s[i];
		for(int j=1;j<i;j++) if(i-j>1||j>1)
			dec(ans[i],2ll*f[j]%mod*s[i-j]%mod);
		for(int k=3;k<i;k++) dec(ans[i],(ll)g[i][k]*ans[k]%mod);
	}
	
	while(Tcase--)
	{
		scanf("%d",&n);
		printf("%d\n",ans[n]);
	}
	
	return 0;
}

先寫這麼多剩下的心情好的時候填坑

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章