Solution P12010 【MX-X10-T6】[LSOT-4] 集合
子任务 4(\(m=1\))
发现 \(m=1\) 时实际上并没有对代表元的限制,因此对于长度为 \(i\) 的 \(n\choose i\) 个区间都可以任意取,答案为:
由于组合数可能很大,因此要对指数取模。根据费马小定理 \(a^{p-1}\equiv1\pmod p\) 可知 \(a^b\bmod P=a^{b\bmod(P-1)}\bmod P\)。
\(P-1=1000000086=2\times3\times41\times59\times68899\)。由于每一项都很小,分别对每个质数使用 lucas 求解组合数然后 crt 合并即可。时间复杂度 \(O(n\log n)\)。
子任务 1(\(m=2\))
这个时候就需要我们对限制有更深刻的认识了。假设当前集合为 \(U\)(\(n=|U|\)),考虑钦定 \(r(U)\) 后会带来什么:
发现对于任意集合 \(S\subsetneqq U,r(U)\in S\),则有 \(r(S)=r(U)\)。因为集合的代表元只能选在集合内部,且 \(S\) 一定会在某种划分方式内取到。
因此所有包含 \(r(U)\) 的 \(S\) 都被确定了。对于其它没有限制的点,发现刚好对应去掉 \(r(U)\) 后 \(n-1\) 的情况。对于 \(n\) 的情况代表元共有 \(n\) 种选择,因此答案为 \(n!\)。
正解
考虑当 \(m\) 变大时会发生什么。此时我们发现除了 \(S\subsetneqq U,r(U)\in S\) 的限制外还有 \(|S|\le n-m+1\),即当 \(S\) 比较大时可能不存在划分方案。
此时又不好做了,设法拓展 \(S\) 的范围。发现上面我们只是考虑了集合 \(U\) 的限制。
当 \(|S|>m\) 时,我们发现 \(S\) 的限制也能派上用场。即 \(T\subsetneqq S,r(S)\in T,|T|\le |S|-m+1\),则 \(r(T)=r(S)\)。再加入 \(U\) 的限制,使 \(r(U)\in T\)(一定找得到 \(|T|\ge 2\) 且满足条件的 \(T\)),那么 \(r(T)=r(U)\)。因此一定有 \(r(S)=r(U)\)。
剩下的情况当 \(n-m+1<|S|\le m\) 时,不存在划分成 \(m\) 段的方案或只存在划分成 \(m\) 个单个元素的方案,都对答案没有限制。因此此时可以任意选择 \(|S|\) 个代表元。答案贡献为:
解决了所有 \(r(U)\in S\) 的集合后,剩下的集合转化到一个 \(n-1\) 的子问题。且当 \(n\) 小到 \(n=m\) 时发现方案数相当于子任务 4 的情况。因此我们有答案:
将底数相同的归到一起,有:
根据 \(\sum_{i=0}^n{i\choose m}={n+1\choose m+1}\) 将上面的组合数求和合并:
然后就可以使用子任务 4 的 lucas 和 crt 做了。时间复杂度 \(O(n+m\log m)\)。
代码
注意特判 \(m=1\) 的情况。
const int P[]={2,3,41,59,68899},MOD=1000000087;
vector<int>fac[5],ifac[5];
il ll fpow(ll x,int n,int P){
ll a=1;while(n){if(n&1)a=a*x%P;x=x*x%P,n>>=1;}return a;
}
il void init(int P,vector<int>&fac,vector<int>&ifac){
fac.resize(P+1),ifac.resize(P+1);
fac[0]=1;forto(i,1,P-1)fac[i]=1ll*fac[i-1]*i%P;
ifac[P-1]=fpow(fac[P-1],P-2,P);forbk(i,P-1,1)ifac[i-1]=1ll*ifac[i]*i%P;
}
il ll C(int n,int m,int p){return m>n?0:(1ll*fac[p][n]*ifac[p][m]%P[p]*ifac[p][n-m]%P[p]);}
il ll lucas(int n,int m,int p){
return m==0?1:(C(n%P[p],m%P[p],p)*lucas(n/P[p],m/P[p],p)%P[p]);
}
il ll C(int n,int m){
int ans=0,P1=MOD-1;
forv(i,5){
ans+=1ll*lucas(n,m,i)*(P1/P[i])%P1*fpow(P1/P[i],P[i]-2,P[i])%P1,ans%=P1;
}
return ans;
}
signed main(){
forv(i,5)init(P[i],fac[i],ifac[i]);
int n=read(),m=read(),ans=1;
if(m==1){
forto(i,1,n)ans=ans*fpow(i,C(n,i),MOD)%MOD;
}else{
forto(i,1,m)ans=ans*fpow(i,C(m,i),MOD)%MOD;
forto(i,m+1,n)ans=1ll*ans*i%MOD;
forto(i,3,m)ans=ans*fpow(i,(C(min(n,i+m-2),i)-C(m,i)+(MOD-1))%(MOD-1),MOD)%MOD;
}
printf("%d\n",ans);
return 0;
}