伪题解[Cnoi2019]最终幻想

这里貌似要写题解,但是不可能的,正解是打表

但是还好这题是个数学题,所以可以记录一下式子。

题面

异常简单,但令人难以动手:

有一个 N N N 维超球, 求使用 K K K N − 1 N-1 N1 维超平面可以将这个 N N N 维超球划分成多少个 N N N 维块。

答案对 998244353 998244353 998244353 取模。

然后我用了半个小时推式子,用了一个小时想写法,最后看题解发现正解是分块打表(脸色渐渐凝固)

然后,用空间想象出 N ≤ 4 , K ≤ 5 N\le 4,K\le 5 N4,K5的情况(脑补),然后类比 N = 4 , K ≤ 5 N=4,K\le 5 N=4,K5的情况,得出我们的表:

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,k1+fn1,k1
这个递推式是很有意义的,我们移项:
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,kfn,k1=fn1,k1
这个 f n , k − f n , k − 1 f_{n,k}-f_{n,k-1} fn,kfn,k1其实就是在已经有 k − 1 k-1 k1 n − 1 n-1 n1维平面时,再用一个 n − 1 n-1 n1维平面切过去最多能切到的碎块的个数。
f n − 1 , k − 1 f_{n-1,k-1} fn1,k1则是在一个 n − 1 n-1 n1维上用 k − 1 k-1 k1 n − 2 n-2 n2维直线划分最多的块数,然后这 k − 1 k-1 k1 n − 2 n-2 n2维直线其实就是前 k − 1 k-1 k1 n − 1 n-1 n1维平面在这个新加进来的平面上的投影(相交的地方)。

你可能感兴趣的:(数论数学,题解)