首页 > 代码库 > bzoj1485:[HNOI2009]有趣的数列

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;
}

 

bzoj1485:[HNOI2009]有趣的数列