有趣的数列 [Codevs 2337,Bzoj 1485,HNOI2009]

45 篇文章 0 订阅
33 篇文章 0 订阅

题目地址请点击——


有趣的数列


【题目描述】

我们称一个长度为 2n 的数列是有趣的,当且仅当该数列满足以下三个条件:

(1)它是从 1 2n 2n 个整数的一个排列 {ai}

(2)所有的奇数项满足 a1<a3<<a2n1 ,所有的偶数项满足 a2<a4<<a2n

(3)任意相邻的两项 a2i1 a2i ( 1in )满足奇数项小于偶数项,即: a2i1<a2i

现在的任务是:对于给定的 n ,请求出有多少个不同的长度为 2n 的有趣的数列。
因为最后的答案可能很大,所以只要求输出答案 mod P 的值。


【输入描述】

输入文件只包含用空格隔开的两个整数 n P


【输出描述】

仅含一个整数,表示不同的长度为 2n 的有趣的数列个数 mod P 的值。


【样例输入】

3 10


【样例输出】

5


【Solution】

我们将这 2n 个数从小到大排个序,然后从小到大决定每一个数放在奇数位还是偶数位。
好比是两个栈,一个装奇数位的数字,一个装偶数位的数字,然后从小到大扫过每个数,决定它放进哪个栈。
这样,我们就可以满足第一、二个条件了。
第三个条件如何满足呢?
我们发现,装偶数位的数字的栈的元素个数一定要小于或等于装奇数位的数字的栈的元素个数。
为什么呢?因为如果出现违背上面那条原则的情况,那么就会出现:第 2k 个数已经放好了,第 2k+2 个数也已经放好了,而第 2k+1 个数还没有放好(因为放在偶数位上的数字个数比放在奇数位上的数字个数要多)。
而我们是从小到大放的数,所以要放在第 2k+1 上的数一定比第 2k 2k+2 个数要大,不符合第三条情况。
如果我们把放在偶数位上视作 y+1 , 放在奇数位上视作 x+1 ,那么放 2n 个数就可以转化为在平面上从 (0,0) 走到 (n,n)
因为时时刻刻 x>=y ,所以相当于走到 (n,n) 且不可以穿过直线 y=x 的路径总数!
根据我之前写的网格这篇博文,

ans=[Cn2nCn12n] mod p=[(2n)!n!n!(2n)!(n+1)!(n1)!] mod p=[(2n)!(n+1)(2n)!n(n+1)!n!] mod p=(2n)!(n+1)!n! mod p=(2n)(2n1)(n+2)n! mod p=(2n1)(2n3)(n+3)((n+2)21)!2(2n(n+2)+1)/2+1 mod p(2n1)(2n3)(n+2)((n+1)2)!2(2n(n+3)+1)/2+1 mod pn0(mod 2),n1(mod 2).

因为取模的 p 不是素数,所以不能用逆元乱搞了。
我们可以用质因数分解的方法,将答案的唯一分解式算出来。
这里我想到了一个类似分治的方法。
我们可以用筛素数的方法,求出每个数 i 的最小质因子 k 和这个质因子是第几个质数 fi
设唯一分解式的第 i 个质数的指数为 ti,则 tfi 加一,然后分治处理 ik
然后就可以 A 了。


【Code】

[cpp]
  1. #include <iostream>  
  2. #include <cstdio>  
  3. #include <cstring>  
  4. #include <ctime>  
  5.   
  6. using namespace std;  
  7.   
  8. #define LL long long  
  9. typedef LL bign;  
  10.   
  11. LL tt[2000010];  
  12. LL prime[2000010];  
  13. LL first[2000010];  
  14. bool no_prime[2000010];   
  15.   
  16. LL n,m;  
  17. bign ans=1;  
  18.   
  19. LL power(LL x,LL y){  
  20.     if(y==0)return 1;  
  21.     if(y==1)return x%m;  
  22.     LL tmp=power(x%m,y/2);  
  23.     if(y&1)return tmp*tmp%m*(x%m)%m;  
  24.     else return tmp*tmp%m;   
  25. }  
  26.   
  27. void make1(LL x){  
  28.     while(1){  
  29.         if(x==1)return;  
  30.         if(prime[first[x]]==x){  
  31.             tt[first[x]]++;  
  32.             return;   
  33.         }  
  34.         else{  
  35.             while(x%prime[first[x]]==0){  
  36.                 tt[first[x]]++;  
  37.                 x/=prime[first[x]];  
  38.             }  
  39.         }  
  40.     }  
  41. }  
  42.   
  43. void make2(LL x){  
  44.     while(1){  
  45.         if(x==1)return;  
  46.         if(prime[first[x]]==x){  
  47.             tt[first[x]]–;  
  48.             return;  
  49.         }  
  50.         else{  
  51.             while(x%prime[first[x]]==0){  
  52.                 tt[first[x]]–;  
  53.                 x/=prime[first[x]];  
  54.             }  
  55.         }  
  56.     }  
  57. }  
  58.   
  59. void Pyh(){  
  60.     if(!(n&1)){  
  61.         tt[1]=(2*n-(n+2)+1)/2+1;  
  62.         for(LL i=n+3;i<=2*n-1;i+=2)make1(i);  
  63.         for(LL i=2;i<=(n+2)/2-1;i++)make2(i);  
  64.         for(LL i=1;i<=prime[0];i++)ans=ans*power(prime[i]%m,tt[i])%m;  
  65.         printf(”%lld\n”,ans%m);  
  66.     }  
  67.     else{  
  68.         tt[1]=(2*n-(n+3)+1)/2+1;  
  69.         for(LL i=n+2;i<=2*n-1;i+=2)make1(i);  
  70.         for(LL i=2;i<=(n+1)/2;i++)make2(i);  
  71.         for(LL i=1;i<=prime[0];i++)ans=ans*power(prime[i],tt[i])%m;  
  72.         printf(”%lld\n”,ans%m);   
  73.     }  
  74. }  
  75.   
  76. int main(){  
  77.       
  78.     scanf(”%lld%lld”,&n,&m);  
  79.     for(LL i=2;i<=2*n;i++){  
  80.         if(!no_prime[i]){  
  81.             prime[++prime[0]]=i;  
  82.             first[i]=prime[0];  
  83.         }  
  84.         LL j=1,t=i*prime[1];  
  85.         while(j<=prime[0]&&t<=2*n){  
  86.             first[t]=j;  
  87.             no_prime[t]=true;  
  88.             if(i%prime[j]==0)break;  
  89.             t=i*prime[++j];  
  90.         }  
  91.     }   
  92.     Pyh();  
  93.     return 0;  
  94. }  
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值