bzoj2111 perm 排列计数

原文链接: http://www.cnblogs.com/znsbc-13/p/11119602.html

 Perm 排列计数

内存限制:512 MiB 时间限制:1000 ms 标准输入输出
 
 

题目描述

称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Magic的,答案可能很大,只能输出模P以后的值

输入格式

输入文件的第一行包含两个整数 n和p,含义如上所述。

输出格式

输出文件中仅包含一个整数,表示计算1,2,N的排列中, Magic排列的个数模 p的值。

样例

样例输入

20 23

样例输出

16

数据范围与提示

100%的数据中,1 ≤  N ≤ 106, P ≤ 10^9,p是一个质数。 数据有所加强

事实上题目少说一句i为奇数/2就向下取整(虽然说不说都可以推断,说了比较严谨)

既然这样我们可以把它想象成一个 二叉堆(小根堆) 满足magic 就是满足二叉堆 那么magic的方案数就是满足二叉堆的方案数。

但如果暴力计算方式会TLE,我们需要用一种别的方式计算

于是就有了树形dp 设f[i]为以i为根的方案数显然可以由两个儿子贡献过来

当前点因为根是最小的所以size[i]-1表示剩余的节点数 然后是在size[i]-1的范围内选size[left]数量的数分配给左儿子

于是

$f[i]=f[(i<<1)]*f[(i<<1|1)]*{C_{size[i]-1}^{size[i<<1]}}$

由于n比较大然后要用lucas定理

然后注意当前点不能大于n转移就完了

下面依然是本人丑陋的代码

 1 #include<bits/stdc++.h>
 2 #define ll long long
 3 #define A  5100000
 4 using namespace std;
 5 ll p,n,m,size[A],f[A],biao[A],ma=0;
 6 ll read()
 7 {
 8     ll f=1,x=0;char c=getchar();
 9     while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
10     while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
11     return f*x;
12 }
13 ll meng(ll x,ll k)
14 {
15     ll ans=1;
16     for(;k;k>>=1,x=x*x%p)
17         if(k&1)
18             ans=x*ans%p;
19     return ans;
20 }
21 ll a(ll n)
22 {
23     if(n<=5000000&&biao[n]) return biao[n];
24     ll ans=1ll;
25     for(ll i=max((ma+1<=n?ma-1:1),1ll);i<=n;i++)        
26         if(!biao[i]){ans=ans*i%p;if(i<=5000000)biao[i]=ans;}
27         else ans=biao[i];
28     if(n<=5000000)ma=n;
29     return ans;
30 }
31 ll jicus(ll n,ll m)
32 {
33     if(m>n) return 0;
34     else return (a(n)*meng(a(m),p-2)%p*meng(a(n-m),p-2))%p;
35 }
36 ll lucas(ll n,ll m)
37 {
38     if(m==0) return 1;
39     return jicus(n%p,m%p)*lucas(n/p,m/p)%p;
40 }
41 void dfs(ll i)
42 {    
43     f[i]=1;
44     size[i]=1;
45     if((i<<1)<=n)
46     {
47         dfs(i<<1);
48         size[i]+=size[i<<1];
49     }
50     if(((i<<1)+1)<=n)
51     {
52         dfs((i<<1)+1);
53         size[i]+=size[i<<1|1];
54     }
55     f[i]=f[(i<<1)<=n?(i<<1):0]%p*f[(i<<1|1)<=n?(i<<1|1):0]%p*lucas(size[i]-1,size[i<<1])%p;
56 }
57 int main()
58 {
59     f[0]=1;
60     n=read(),p=read();
61     dfs(1);
62     cout<<f[1]<<endl;
63 }
View Code

 当然也可以线性转移

    for(int i=n;i;--i)
    {
        if(sz[i]<3) dp[i]=1;
        else dp[i]=1ll*dp[lch]*dp[rch]%mod*lucas(sz[i]-1,sz[lch])%mod;
        //不能给右儿子算组合数,可能越界
    }

会快很多

转载于:https://www.cnblogs.com/znsbc-13/p/11119602.html

猜你喜欢

转载自blog.csdn.net/weixin_30642561/article/details/95293882