码迷,mamicode.com
首页 > 其他好文 > 详细

P5520 [yLOI2019] 青原樱

时间:2020-06-09 09:19:11      阅读:63      评论:0      收藏:0      [点我收藏+]

标签:oid   组合数   ini   之间   efi   如何   前缀   质数   space   

部分分比较多,依次讲一讲。

因为带编号,所以最后答案要乘上 \(m!\)

对于子任务 1,直接输出 1%p,时间复杂度 \(O(1)\)

对于子任务 2,dfs 枚举每个位置是否种植,时间复杂度 \(O(2^n)\)

对于子任务 3,设 \(f_{i,j}\) 为种了 \(i\) 株幼苗,最后一株种在位置 \(j\) 的方案数。

\[f_{i,j}=\sum_{k<j-1}f_{i-1,k} \]

直接转移即可,时间复杂度 \(O(n^2m)\)

对于子任务 4,易发现子任务 3 中的 DP 可以用前缀和优化到 \(O(nm)\)

对于子任务 5,我们发现 DP 难以解决了,因为状态无论如何都高达 \(nm\),于是可以考虑组合数直接计算。

考虑合法的方案,相邻的两株幼苗之间至少有一个空位。

拉出 \(m-1\) 个空位,剩下 \(n-m+1\) 个位置随便种,然后在每两株幼苗之间插入一个空位,这样一定是合法且唯一的。

所以总方案数就是 \(\binom{n-m+1}{m}\),因为模数是质数所以可以用逆元做,时间复杂度 \(O(n+\log p)\)

对于子任务 6,发现最终的答案就是 \(\binom{n-m+1}{m}\times m!=\frac{(n-m+1)!m!}{m!(n-m+1-m)!}=\frac{(n-m+1)!}{(n+1-2m)!}=A_{n-m+1}^m\)

直接算,时间复杂度 \(O(m)\)

code:

#include<bits/stdc++.h>
using namespace std;
#define N 2005
#define Max(x,y)((x)>(y)?x:y)
#define For(i,x,y)for(i=x;i<=(y);i++)
int n,m,p;
namespace Subtask1
{
	inline void init()
	{
		cout<<1%p;
	}
}
namespace Subtask2
{
	int tot;
	void dfs(int pos,int num)
	{
		if(pos>n)
		{
			if(!num)tot++;
			return;
		}
		dfs(pos+1,num);
		if(num)dfs(pos+2,num-1);
	}
	void init()
	{
		int i;
		dfs(1,m);
		For(i,2,m)tot*=i;
		cout<<tot%p;
	}
}
namespace Subtask3
{
	int f[205][405],tot;
	void init()
	{
		int i,j,k;
		f[0][0]=1;
		For(i,1,m)
		For(j,(i<<1)-1,n)
		For(k,Max((i<<1)-3,0),Max(j-2,0))f[i][j]=(f[i][j]+f[i-1][k])%p;
		For(i,(m<<1)-1,n)tot=(tot+f[m][i])%p;
		For(i,2,m)tot=1LL*tot*i%p;
		cout<<tot;
	}
}
namespace Subtask4
{
	int f[N][N];
	void init()
	{
		int i,j;
		For(i,0,n)f[0][i]=1;
		For(i,1,m)
		For(j,(i<<1)-1,n)f[i][j]=(f[i][j-1]+f[i-1][Max(j-2,0)]-(!Max((i<<1)-3,0)?0:f[i-1][Max((i<<1)-3,0)-1]))%p;
		For(i,2,m)f[m][n]=1LL*f[m][n]*i%p;
		cout<<f[m][n];
	}
}
namespace Subtask5
{
	int tot=1;
	int ksm(int x,int y)
	{
		return(!y?1:1LL*ksm(1LL*x*x%p,y>>1)*(y&1?x:1)%p);
	}
	int C(int x,int y)
	{
		int tmp=1,i;
		For(i,2,y)tmp=1LL*tmp*i%p;
		For(i,2,x-y)tmp=1LL*tmp*i%p;
		tmp=ksm(tmp,p-2);
		For(i,2,x)tmp=1LL*tmp*i%p;
		return tmp;
	}
	void init()
	{
		int i;
		For(i,2,m)tot=1LL*tot*i%p;
		cout<<1LL*C(n-m+1,m)*tot%p;
	}
}
namespace Subtask6
{
	int P(int x,int y)
	{
		int tot=1;
		while(y--)tot=1LL*tot*x--%p;
		return tot;
	}
	inline void init()
	{
		cout<<P(n-m+1,m);
	}
}
int main()
{
	int type;
	cin>>type>>n>>m>>p;
	switch(type)
	{
		case 0:Subtask1::init();
		break;
		case 1:Subtask2::init();
		break;
		case 2:Subtask3::init();
		break;
		case 3:Subtask4::init();
		break;
		case 4:Subtask5::init();
		break;
		case 5:Subtask6::init();
		break;
	}
	return 0;
}

分包写了,应该很清楚吧 qwq。

P5520 [yLOI2019] 青原樱

标签:oid   组合数   ini   之间   efi   如何   前缀   质数   space   

原文地址:https://www.cnblogs.com/May-2nd/p/13068608.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!