欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

P2523 [HAOI2011]Problem c/ P1386 座位安排

程序员文章站 2022-07-15 16:16:19
...

R e s u l t Result Result

好家伙,双倍经验,连数据都一模一样呢
P2523 [HAOI2011]Problem c/ P1386 座位安排
P2523 [HAOI2011]Problem c/ P1386 座位安排


H y p e r l i n k Hyperlink Hyperlink

https://www.luogu.com.cn/problem/P1386
https://www.luogu.com.cn/problem/P2523


D e s c r i p t i o n Description Description

n n n个人排座位,如果第 i i i个人座位被占了,则它会坐在这个位置后面的第一个没被占的位置 [ i + 1 , n ] [i+1,n] [i+1,n],如果没有,则这种方案是不合法的

现在有 m m m个人已经钦定了它们的座位,问合法的坐座位方案数,答案对 M M M取模

数据范围: n , m ≤ 300 , M ≤ 1 0 9 n,m\leq 300,M\leq 10^9 n,m300,M109


S o l u t i o n Solution Solution

首先座位的钦定与具体是哪个人无关,我们只关心还剩下多少的座位

那么就可以 d p dp dp了, l a s t i last_i lasti表示 i i i这个位置后面还剩余多少位置(包括 i i i),容易发现对位置做一个后缀和即可得到 l a s t i = n − i + 1 − s u m i last_i=n-i+1-sum_i lasti=ni+1sumi

f i , j f_{i,j} fi,j表示排到了第 i i i个座位(倒着排),后面已经有 j j j个人确定了位置

初态: f n + 1 , 0 = 1 f_{n+1,0}=1 fn+1,0=1
转移: f i , j = ∑ j = 0 l a s t i ∑ k = 0 j f i + 1 , j − k C j k f_{i,j}=\sum_{j=0}^{last_i}\sum_{k=0}^j f_{i+1,j-k}C_j^k fi,j=j=0lastik=0jfi+1,jkCjk
终态: f 1 , n − m f_{1,n-m} f1,nm

注意特判 l a s t i < 0 last_i<0 lasti<0的情况,这样是不可能排得了座位的

时间复杂度: O ( T n 3 ) O(Tn^3) O(Tn3)


C o d e Code Code

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define LL long long
#define N 310
using namespace std;int n,m,mod,T;
LL f[N][N],s[N],C[N][N];
inline LL read()
{
	LL d=1,f=0;char c;
	while(c=getchar(),!isdigit(c)) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
	while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
	return d*f;
}
inline bool tp(){for(register int i=1;i<=n;i++)if(s[i]>n-i+1) return true;return false;}
signed main()
{
	T=read();
	while(T--)
	{
		memset(f,0,sizeof(f));
		memset(s,0,sizeof(s));
		n=read();m=read();mod=read();
		C[0][0]=C[1][0]=C[1][1]=1;
		for(register int i=2;i<=n;i++){C[i][0]=1;for(register int j=1;j<=i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;}
		for(register int i=1;i<=m;i++) read(),s[read()]++;
		for(register int i=n;i;i--) s[i]+=s[i+1];
		if(tp()) {puts("NO");continue;}
		f[n+1][0]=1;
		for(register int i=n;i;i--) for(register int j=0;j<=n-i+1-s[i];j++) for(register int k=0;k<=j;k++)
		f[i][j]=(f[i][j]+f[i+1][j-k]*C[j][k]%mod)%mod;
		printf("YES %lld\n",f[1][n-m]);
	}
}