一句话题意
给定1到n求其全排列中没有三个及以上递增(减)的方案数,ans%p(p不一定为素数)
错误打法一
我看到这道题后首先想到的是容斥:
ans=全排列-至少3个不满足的+至少4个......
但是考虑不到有两个不连续的递增(减)的情况,所以WA0了。
错误打法二
f[i][0]代表前i个已经处理好了且下一个该是山谷的方案,1同理。
假如有一个这样的数列(i=8)
3 2 7 4 5 6 1
j
4 5 6不满足条件,所以要把8往4 5之间放,所以便有
f[i][(i-j-1)&1]+=f[j][1]*f[i-j-1][(i-j-1)&1];
但是我们会发现我们不能考虑到i放的地方前一个和后一个的大小所以这个又崩了......
正解
其实不用考虑往中间插,只要考虑往末尾放就行,即:
f[i][k&1]+=f[j][1]*f[k][k&1];(k=i-j-1)
呃...样例又没过。哦哦,忘记考虑i-1个元素的排列了。
最终式子:
f[i][k&1]+=c[i-1][j]*f[j][1]*f[k][k&1];(k=i-j-1)
因为p不保证是质数,所以组合用杨辉三角递推即可。
#include<iostream> #include<cstdio> #define ll long long using namespace std; ll ans,n,p,f[5000][2],a[5000][5000]; int main() { scanf("%lld%lld",&n,&p); a[0][0]=1; for(int i=1;i<=n;i++) { a[i][0]=1; for(int j=1;j<=i+1;j++) { a[i][j]=(a[i-1][j]+a[i-1][j-1])%p; } } f[0][0]=f[0][1]=f[1][1]=f[1][0]=1; for(int i=2;i<=n;i++) { for(int j=0;j<i;j++) { ll k=i-j-1; (f[i][k&1]+=a[i-1][j]*f[j][1]%p*f[k][k&1]%p)%=p; } } ans=(f[n][1]+f[n][0])%p; printf("%lld",ans); return 0; }