Topcoder CyclesNumber 和 ARC96E Everything on It
CyclesNumber
求所有 \(n\) 个点的置换的轮换个数 \(m\) 次⽅的和。
\(T\leq 300,n \leq 10^5,m\leq 300\)。
题解
https://www.cnblogs.com/jefflyy/p/9425348.html
前置知识
根据《具体数学》,斯特林数有性质:
这两个公式的组合意义很显然,并且应该可以用归纳法证明。
还有一个类似的式子:
但它并没有什么组合意义,尝试用归纳法证明它。
注意到组合数的第二维不同,因此首要任务是把组合数拼起来。
斯特林套路
这道题求的是:
\(O(nm)\) 预处理第一类斯特林数,\(O(m^2)\) 预处理第二类斯特林数,就可以 \(O(m)\) 回答一个询问了
Topcoder的评测机很垃圾,感觉在给我随机打分。前后相差只有一行注释的两次提交都能得不同的分。
要想AC这题必须要把阶乘预处理到 \(n\),我也不知道为什么。
int S1[100010][310],S2[310][310],fac[100010];
void init(int n,int m){
S1[0][0]=1;
for(int i=1;i<=n+1;++i)for(int j=1;j<=min(i,m+1);++j)
S1[i][j]=add(S1[i-1][j-1],mul(i-1,S1[i-1][j]));
S2[0][0]=1;
for(int i=1;i<=m;++i)for(int j=1;j<=i;++j)
S2[i][j]=add(S2[i-1][j-1],mul(j,S2[i-1][j]));
fac[0]=1;
for(int i=1;i<=n;++i) fac[i]=mul(fac[i-1],i);
}
int solve(int n,int m){
if(m==0) return fac[n];
int ans=0;
for(int i=0;i<=m;++i) ans=add(ans,mul(S2[m][i],mul(fac[i],S1[n+1][i+1])));
return ans;
}
class CyclesNumber{
public:
vector<int> getExpectation(vector<int> n,vector<int> m){
init(1e5,300);
vector<int> ans(n.size());
for(int i=0;i<(int)n.size();++i) ans[i]=solve(n[i],m[i]);
return ans;
}
};
Everything on It
求有多少个子集族,满足:
-
其中任意一个子集都是 \([n]\) 的子集;
-
任意两个子集互不相同;
-
\(1, 2, · · · , n\) 都在其中至少出现了 \(2\) 次。
答案对 \(M\) 取模。\(2 ≤ N ≤ 3000, 10^8 ≤ M ≤ 10^9 + 9\),\(M\) 是质数。
题解
戴大爷教会我推式子不必一步到位。
其中 \(F(i)\) 表示 \(1\sim i\) 出现次数小于等于 \(1\) 的方案数。
如何求 \(F(i)\) 呢?我们显然可以把所有集合分为两部分:含有 \(1\sim i\) 的和不含 \(1\sim i\) 的。我们可以把方案按照第一类集合的数量分类计算。
其中 \(G(i,j)\) 表示把分 \(1\sim i\) 入 \(j\) 个非空盒子,但是有的球可以扔掉的方案数。显然有
戴大爷的组合意义:
可以在 \(j\) 个集合之外新增一个集合表示垃圾堆,但是这个集合是可以为空的。怎么办呢?再新增一个 \(0\) 号球,这个球在哪个集合里面就说明哪个集合是垃圾堆。容易发现原方案与新构造的方案一一对应。
这个事情告诉我们,只要具体数学学得好,推公式那都不是事儿。什么构造,不存在的。
时间复杂度 \(O(n^2\log m)\)。
CO int N=3000+10;
int C[N][N],S[N][N],B[N];
int main(){
int n=read<int>();read(mod);
for(int i=0;i<=n;++i){
C[i][0]=C[i][i]=1;
for(int j=1;j<i;++j) C[i][j]=add(C[i-1][j-1],C[i-1][j]);
}
S[0][0]=1;
for(int i=1;i<=n+1;++i)for(int j=1;j<=i;++j)
S[i][j]=add(S[i-1][j-1],mul(j,S[i-1][j]));
B[0]=1;
for(int i=1;i<=n;++i){
B[i]=B[i-1]<<1;
if(B[i]>=mod-1) B[i]-=mod-1;
}
int ans=0;
for(int i=0;i<=n;++i){
int sum=0;
for(int j=0;j<=i;++j) sum=add(sum,mul(S[i+1][j+1],fpow(2,(n-i)*j)));
sum=mul(sum,mul(C[n][i],fpow(2,B[n-i])));
ans=add(ans,i&1?mod-sum:sum);
}
printf("%d\n",ans);
return 0;
}