但是还好这题是个数学题,所以可以记录一下式子。
异常简单,但令人难以动手:
有一个 N N N 维超球, 求使用 K K K 个 N − 1 N-1 N−1 维超平面可以将这个 N N N 维超球划分成多少个 N N N 维块。
答案对 998244353 998244353 998244353 取模。
然后我用了半个小时推式子,用了一个小时想写法,最后看题解发现正解是分块打表(脸色渐渐凝固)
然后,用空间想象出 N ≤ 4 , K ≤ 5 N\le 4,K\le 5 N≤4,K≤5的情况(脑补),然后类比 N = 4 , K ≤ 5 N=4,K\le 5 N=4,K≤5的情况,得出我们的表:
1 | 1 | 1 | 1 | 1 |
---|---|---|---|---|
2 | 3 | 4 | 5 | 6 |
2 | 4 | 7 | 11 | 16 |
2 | 4 | 8 | 15 | 26 |
2 | 4 | 8 | 16 | 31 |
2 | 4 | 8 | 16 | 32 |
然后上下作差:
1 | 1 | 1 | 1 |
---|---|---|---|
1 | 2 | 3 | 4 |
0 | 1 | 3 | 6 |
0 | 0 | 1 | 4 |
0 | 0 | 0 | 1 |
0 | 0 | 0 | 0 |
这玩意就是二项式函数。
式子就显而易见了: ∑ i = 0 n ( i k ) \sum_{i=0}^{n}(^k_i) ∑i=0n(ik)
神奇!
非满分代码:
#include
using namespace std;
template<typename tn> void read(tn &a){
tn x=0,f=1;char c=' ';
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
a=x*f;
}
int n,k;
const int N=1e6+5;
const int mod=998244353;
int poww(int a,int b){
int ans=1;
while(b){
if(b&1) ans=1ll*ans*a%mod;
a=1ll*a*a%mod;
b>>=1;
}
return ans;
}
int jc[N],njc[N];
int C(int n,int m){
return 1ll*jc[n]*njc[m]%mod*njc[n-m]%mod;
}
int ans;
int main(){
jc[0]=njc[0]=1;
for(int i=1;i<N;i++) jc[i]=1ll*i*jc[i-1]%mod,njc[i]=1ll*poww(i,mod-2)*njc[i-1]%mod;
read(n);
read(k);
if(n>=k){
printf("%d\n",poww(2,k));
return 0;
}
for(int i=0;i<=n;i++) ans=(ans+C(k,i))%mod;
printf("%d\n",ans);
return 0;
}
/*
1 1 1 1 1
2 3 4 5 6
2 4 7 11 16
2 4 8 15 26
2 4 8 16 31
2 4 8 16 32
*/
但是并没有结束,我们从我们打的表中可以轻易地看出来这一个递推式:
f n , k = f n , k − 1 + f n − 1 , k − 1 f_{n,k}=f_{n,k-1}+f_{n-1,k-1} fn,k=fn,k−1+fn−1,k−1
这个递推式是很有意义的,我们移项:
f n , k − f n , k − 1 = f n − 1 , k − 1 f_{n,k}-f_{n,k-1}=f_{n-1,k-1} fn,k−fn,k−1=fn−1,k−1
这个 f n , k − f n , k − 1 f_{n,k}-f_{n,k-1} fn,k−fn,k−1其实就是在已经有 k − 1 k-1 k−1个 n − 1 n-1 n−1维平面时,再用一个 n − 1 n-1 n−1维平面切过去最多能切到的碎块的个数。
而 f n − 1 , k − 1 f_{n-1,k-1} fn−1,k−1则是在一个 n − 1 n-1 n−1维上用 k − 1 k-1 k−1个 n − 2 n-2 n−2维直线划分最多的块数,然后这 k − 1 k-1 k−1个 n − 2 n-2 n−2维直线其实就是前 k − 1 k-1 k−1个 n − 1 n-1 n−1维平面在这个新加进来的平面上的投影(相交的地方)。