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

I I I
感觉这题非常厉害
为了方便解释,做以下约定
1. 1. 1.将全序列 p 1 , p 2 . . . p n p_1,p_2...p_n p1,p2...pn记为 A A A
2. 2. 2. A A A的某个严格子区间记作 B i B_i Bi

题目把 interval 定义为值域大小和区间大小相等的 B i B_i Bi
我们定义maximal interval为不被任意一个interval完整包含的interval

有一个重要结论:如果两个maximal interval B x , B y B_x,B_y Bx,By相交,那么区间 B x ∪ B y B_x∪B_y BxBy一定符合interval的定义,又因为 B x , B y B_x,B_y Bx,By是maximal interval,所以若他们相交,那么并一定就是 A A A

考虑算出 n n n的全排列里所有不合法的排列
依据这个结论,我们可以将不合法的排列分为两类
类型 1 : 1: 1存在两个maximal interval B u , B v B_u,B_v Bu,Bv,使 B u ∪ B v = A B_u∪B_v=A BuBv=A
类型 2 : 2: 2A为大于等于3个maximal interval挨着连在一起组成

我们先考虑类型 1 1 1,它可以分为 B u B_u Bu B v B_v Bv相交或相邻两种情况
B u B_u Bu B v B_v Bv相交,记
B w = B u ∩ B v , B u ′ = B u − B w , B v ′ = B v + B w B_w=B_u∩B_v,B_{u'}=B_u-B_w,B_{v'}=B_v+B_w Bw=BuBv,Bu=BuBw,Bv=Bv+Bw
转化完后得到的 B u ′ 和 B v ′ B_{u'}和B_{v'} BuBv是相邻的,因此我们只要考虑 B u , B v B_u,B_v Bu,Bv相邻的情况

显然可以发现 B u , B v B_u,B_v Bu,Bv的值域分别为 [ 1 , k ] , [ k + 1 , n ] [1,k],[k+1,n] [1,k],[k+1,n](证明可以用反证)
不妨假设 B u B_u Bu B v B_v Bv的左侧(右侧的情况方案数是相同的)

为了避免重复计数,我们强制令 B u B_u Bu的prefix中除了自己外,没有任何一个prefix满足值域为 [ 1 , k ] [1,k] [1,k]且为interval (若存在这样的prefix,可以通过上面那个相交变相邻的转化转为不存在)

那么就可以统计数量了,记 f n f_n fn n n n的所有排列 A A A中满足 任何一个prefix都不满足值域为 [ 1 , k ] [1,k] [1,k]且为interval 的排列数量
f n = n ! − ∑ i = 1 n − 1 ( f i ∗ ( n − i ) ! ) f_n=n!-\sum_{i=1}^{n-1}(f_i*(n-i)!) fn=n!i=1n1(fi(ni)!)

接着考虑类型 2 2 2
对于某个maximal interval B u B_u Bu,他内部是任意排的,我们唯一要满足的限制是不存在某一段连续的maximal interval可以拼成一个interval
这个限制相当于这些maximal interval的排列是interval-free的
由于我们统计的是非法方案,因此至少存在一个maximal interval B v B_v Bv的大小是>=2的,于是这些maximal interval的个数<n,就化成了子问题
为了计算这种的方案数,需要一个辅助的dp

g n , k g_{n,k} gn,k为将 n n n划分成 k k k段,每段内部任意排列的方案数
g n , k = ∑ i = k − 1 n − 1 g i , k − 1 ∗ ( n − i ) ! g_{n,k}=\sum_{i=k-1}^{n-1}g_{i,k-1}*(n-i)! gn,k=i=k1n1gi,k1(ni)!

然后就可以算答案了,记 a n s n ans_n ansn n n n的所有排列 A A A中interval-free的排列数
a n s n = n ! − 2 ∑ i = 1 n − 1 f i ∗ ( n − i ) ! − ∑ k = 3 n − 1 g n , k ∗ a n s k ans_n=n!-2\sum_{i=1}^{n-1}f_i*(n-i)!-\sum_{k=3}^{n-1}g_{n,k}*ans_k ansn=n!2i=1n1fi(ni)!k=3n1gn,kansk

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;
}

先写这么多剩下的心情好的时候填坑

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值