bzoj1485:[HNOI2009]有趣的数列
思路:首先限制数很多,逐步来考虑,限制一很容易满足,考虑限制二,也就是让奇数位和偶数位上的数递增,限制三就是让奇数位上的数小于奇数位加一对应的偶数位上的数,那么我们可以把形成序列的过程看成加数的过程,从小到大逐步加(这显然满足限制一),然后加数的条件一是从小到大依次放奇数位或偶数位,因此也满足限制二,然后无论何时奇数位上的数一定要大于等于偶数位上的数,这样也满足了限制三,那么问题就转化成了按照如上条件放数的方案数,联系第二个条件,也就是无论何时奇数位上的数一定要大于等于偶数位上的数,联想到了什么?没错,就是栈,进栈次数一定要大于等于出栈次数,不妨把放在奇数位上看成进栈,放在偶数位上看成出栈,这样就是求一个出栈的序列,也就是Catalan数列。
然后p不一定是素数,也就是不能求逆元,那么就可以分解质因数,乘一个数相当于把一个数的所有质因数乘一遍,就可以用一个数组记录一下所有数被乘了多少遍,那么乘就是加一,除就是减一,然后n最大为1e6,暴力求每个数的质因数是n*sqrt(n)的,显然会T(然而我还傻乎乎地交了一遍,看到跑了那么久猛然醒悟过来。。。。。),于是就可以线筛统计出每个数的最小质因数,然后求每个数的所有质因数时每次除以最小质因数就好了,然后最小质因数一定>=2,因此这样是nlogn的(而且一点也不严格),然后就可以飞快跑过本题。
#include<bits/stdc++.h> using namespace std; #define maxn 3000005 int n,p,ans=1,tot; int f[maxn],prime[maxn],pos[maxn]; bool isprime[maxn]; int read(){ int x=0,f=1;char ch=getchar(); for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1; for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; return x*f; } void change(int x,int val){ for (;x!=1;) f[prime[pos[x]]]+=val,x/=prime[pos[x]]; } int power(int a,int k,int p){ int x=1; for (;k;k>>=1,a=1ll*a*a%p) if (k&1) x=1ll*x*a%p; return x; } int main(){ n=read(),p=read();memset(isprime,1,sizeof(isprime)); for (int i=2;i<=2*n;i++){ if (isprime[i]) prime[++tot]=i,pos[i]=tot; for (int j=1;i*prime[j]<=2*n&&j<=tot;j++){ isprime[i*prime[j]]=0,pos[i*prime[j]]=j; if (i%prime[j]==0) break; } } for (int i=2*n;i>n+1;i--) change(i,1); for (int i=1;i<=n;i++) change(i,-1); for (int i=1;i<=tot;i++) if (f[prime[i]]) ans=1ll*ans*power(prime[i],f[prime[i]],p)%p; printf("%d\n",ans); return 0; }