标签:oid 组合数 ini 之间 efi 如何 前缀 质数 space
部分分比较多,依次讲一讲。
因为带编号,所以最后答案要乘上 \(m!\)。
对于子任务 1,直接输出 1%p
,时间复杂度 \(O(1)\)。
对于子任务 2,dfs 枚举每个位置是否种植,时间复杂度 \(O(2^n)\)。
对于子任务 3,设 \(f_{i,j}\) 为种了 \(i\) 株幼苗,最后一株种在位置 \(j\) 的方案数。
直接转移即可,时间复杂度 \(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。
标签:oid 组合数 ini 之间 efi 如何 前缀 质数 space
原文地址:https://www.cnblogs.com/May-2nd/p/13068608.html