组合数学2024蓝桥杯深度优先搜索 蓝桥杯 2024 国研究生组
WZH [蓝桥杯 2024 国研究生组] 深度优先搜索
我不会卡特兰数。
给定长度 n 的序列 a 满足 ai∈[−1,n],求有多少种将 −1 替换为 [0,n] 任意数的方案使得:
- 有且仅有 a1=0;
- 对于任意一个 2≤i≤n,有 ai−1+1≥ai。
对 109+7 取模。
套路的差分,令 bi=ai−ai−1,则 bi≤1。
不妨分段考虑,即,考虑 a0,an=−1,ai=−1的弱化版。
那么首先我们有限制:
a0+∑bi=an
由于 bi≤1 不太好处理,我们不妨令 ci=1−bi,这样限制变为 ci≥0,然后:
a0=∑1−ci=an
接着 si 为 ci 的前缀和,那么上面的约束变为 sn=a0−an+n。
然而这还不够,我们还有这个限制:
a0+j≤i∑bi≥1
于是,将 si 代换 bi 后得到 si≤i+a0−1。
接下来最精彩的一步!将上述限制转化为如下场景:
初始在 (0,0),每次向右边走一步,然后向上走 ci 步,不能碰到直线 y=x+a0,终点 (n,sn),求方案数。
这是一个经典的反射容斥问题,使用你精湛的反射容斥技巧就能解决。
上述简化版解决后,剩下的就是分段转移了,十分简单。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| #include <bits/stdc++.h> using namespace std; const int N=3001000,P=1000000007; int n,a[N],fac[N],inv[N],am[N],ss; int kpow(int a,int b){ int t=1; while(b){ if(b&1)t=1ll*t*a%P; a=1ll*a*a%P; b>>=1; } return t; } void init(int n){ fac[0]=1; for(int i=1;i<=n;i++)fac[i]=1ll*fac[i-1]*i%P; inv[n]=kpow(fac[n],P-2); for(int i=n-1;~i;i--)inv[i]=1ll*inv[i+1]*(i+1)%P; } int C(int n,int m){return n<m?0:1ll*fac[n]*inv[m]%P*inv[n-m]%P;} int calc(int l,int r){ if(l+1==r)return a[l]+1>=a[r]?1:0; return (C(a[l]-a[r]+2*(r-l)-1,r-l-1)-C(a[l]-a[r]+2*(r-l)-1,r-l-1-a[r])+P)%P; } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",a+i); init(n*3); if(a[1]!=0&&a[1]!=-1)return puts("0"),0; a[1]=0; for(int i=1;i<=n;i++)if(a[i]>=0)am[++ss]=i; int ans=1; for(int i=2;i<=ss;i++)ans=1ll*ans*calc(am[i-1],am[i])%P; if(a[n]==-1){ int res=0; for(int i=1;i<=n;i++) a[n]=i,(res+=calc(am[ss],n))%=P; ans=1ll*ans*res%P; } printf("%d",ans); return 0; }
|