[海军国际项目办公室]简单题

简单题

题面

在这里插入图片描述
在这里插入图片描述

题解

一般取这种标题的都不是什么好题。

看到这种题,我们首先很容易考虑到 d p dp dp。
我们定义 d p i , j , k dp_{i,j,k} dpi,j,k​表示选了 i i i个数, a a a的和为 j j j, b b b的和为 k k k时的答案。
容易得到递推式:
d p i , j , k = ∑ x = 1 j ∑ y = 1 k min ⁡ ( x , y ) d p i − 1 , j − x , k − y dp_{i,j,k}=\sum_{x=1}^{j}\sum_{y=1}^{k}\min(x,y)dp_{i-1,j-x,k-y} dpi,j,k​=x=1∑j​y=1∑k​min(x,y)dpi−1,j−x,k−y​
很明显直接这样做是 O ( T k n 2 m 2 ) O\left(Tkn^2m^2\right) O(Tkn2m2),大概可以拿 20 p t s 20pts 20pts。
但事实上上面的式子的系数至于 x , y x,y x,y中较小的一个有关,我们可以尝试枚举 x x x或 y y y中某一个作为较小的一个,通过前锥和转移。
这样做的话是 O ( T k n m ( n + m ) ) O\left(Tknm(n+m)\right) O(Tknm(n+m))的,可以拿到 40 p t s 40pts 40pts。
但我们很容易发现,前面的系数是针对 j , k j,k j,k这两维的,这两维从某种形式上说是等价的。
所以 d p i − 1 , j ′ , k ′ dp_{i-1,j',k'} dpi−1,j′,k′​对 d p i , j , k dp_{i,j,k} dpi,j,k​的贡献实际上是 min ⁡ ( j − j ′ , k − k ′ ) \min(j-j',k-k') min(j−j′,k−k′),我们可以考虑通过差分的形式来维护。
d p i − 1 , j ′ , k ′ dp_{i-1,j',k'} dpi−1,j′,k′​对 d p i , j , k dp_{i,j,k} dpi,j,k​的贡献的系数是等价于函数 { x = j ′ + Δ y = k ′ + Δ ( Δ > 0 ) \left\{ \begin{array}{cc}x=j'+\Delta\\ y=k'+\Delta\end{array}\right .(\Delta>0) {x=j′+Δy=k′+Δ​(Δ>0)在 ( 0 , 0 ) (0,0) (0,0)与 ( j , k ) (j,k) (j,k)构成的矩形中所经过的整点数。
同样,一次转移中, d p i , j , k dp_{i,j,k} dpi,j,k​处相较于它前面的点新增的贡献就是从它往斜上方走的那条斜线上的点的 d p dp dp值之和,我们可以对于每个点都记录下他的新增转移。
点 j , k j,k j,k处新增转移我们记作 d i f i , j , k dif_{i,j,k} difi,j,k​,很明显有
d i f i , j , k = d i f i , j − 1 , k − 1 + d p i − 1 , j − 1 , k − 1 dif_{i,j,k}=dif_{i,j-1,k-1}+dp_{i-1,j-1,k-1} difi,j,k​=difi,j−1,k−1​+dpi−1,j−1,k−1​
故, d p dp dp值的转移等价于
d p i , j , k = d p i , j − 1 , k + ∑ l = 1 k d i f i , j , l dp_{i,j,k}=dp_{i,j-1,k}+\sum_{l=1}^{k}dif_{i,j,l} dpi,j,k​=dpi,j−1,k​+l=1∑k​difi,j,l​
后面的部分可以用前缀和进行优化,这样,我们的时间复杂度就是 O ( T k n m ) O\left(Tknm\right) O(Tknm)了。
虽然我们不可能将大小为 500 × 500 × 500 500\times500\times500 500×500×500的 d p dp dp数组都记录下来,但我们完全可以先将询问离线,对于每个 k k k得到 d p k , i , j dp_{k,i,j} dpk,i,j​时记录 k k k值为这个询问的答案即可。
这样,我们就可以将 T T T去掉了,时间复杂度 O ( k n m + T ) O\left(knm+T\right) O(knm+T),有 70 p t s 70pts 70pts。

我们的 d p dp dp貌似已经无法继续优化了,考虑用其它方法求我们的答案,恰好上面的式子很适合我们使用多项式。
我们记二元生成函数 f i ( j , k ) f_{i}(j,k) fi​(j,k)表示 d p i , j , k dp_{i,j,k} dpi,j,k​的值,由于我们 f i f_{i} fi​转移到 f i + 1 f_{i+1} fi+1​每次都是加入一个数,我们沿用上面的转移式子:
f 0 = 1 , f i = f i − 1 ⋅ g = g i f_{0}=1,f_{i}=f_{i-1}\cdot g=g^i f0​=1,fi​=fi−1​⋅g=gi
可以得到多项式 g g g的表达式,
g = ∑ i = 1 ∞ ∑ j = 1 ∞ min ⁡ ( i , j ) x i y j = ∑ k = 1 ∞ ∑ i = k ∞ ∑ j = k ∞ x i y j = ∑ k = 1 ∞ ( ∑ i = k ∞ x i ) ⋅ ( ∑ j = k ∞ y j ) g=\sum_{i=1}^{\infty}\sum_{j=1}^{\infty}\min(i,j)x^iy^j=\sum_{k=1}^{\infty}\sum_{i=k}^{\infty}\sum_{j=k}^{\infty}x^iy^j=\sum_{k=1}^{\infty}(\sum_{i=k}^{\infty}x^i)\cdot (\sum_{j=k}^{\infty}y^j) g=i=1∑∞​j=1∑∞​min(i,j)xiyj=k=1∑∞​i=k∑∞​j=k∑∞​xiyj=k=1∑∞​(i=k∑∞​xi)⋅(j=k∑∞​yj)
代入总所周知的式子 ∑ i = 0 ∞ x i = 1 1 − x \sum_{i=0}^{\infty}x^i=\frac{1}{1-x} ∑i=0∞​xi=1−x1​,可得,
g = ∑ k = 1 ∞ x k 1 − x ⋅ y k 1 − y = ∑ k = 1 ∞ x k y k ( 1 − x ) ( 1 − y ) = x y 1 − x y ( 1 − x ) ( 1 − y ) = x y ( 1 − x ) ( 1 − y ) ( 1 − x y ) g=\sum_{k=1}^{\infty}\frac{x^k}{1-x}\cdot \frac{y^k}{1-y}=\frac{\sum_{k=1}^{\infty}x^ky^k}{(1-x)(1-y)}=\frac{\frac{xy}{1-xy}}{(1-x)(1-y)}=\frac{xy}{(1-x)(1-y)(1-xy)} g=k=1∑∞​1−xxk​⋅1−yyk​=(1−x)(1−y)∑k=1∞​xkyk​=(1−x)(1−y)1−xyxy​​=(1−x)(1−y)(1−xy)xy​
得到上面这个式子,如果我们用二元函数的卷积展开去做(就是将转化成 n n n进制的数去做一次函数的卷积),时间复杂度是 O ( n m log ⁡   k log ⁡   n m ) O\left(nm\log\,k\log\,nm\right) O(nmlogklognm)的,大概可以过掉 80 p t s 80pts 80pts。
然而实际上我们的 C 2020 j z m \color{black}{C}\color{red}{2020jzm} C2020jzm(其真实实力的颜色)巨佬(即OneInDark巨佬)还推出了一个奇怪的式子, d p dp dp得到了 80 p t s 80pts 80pts。
但是我们明显还有更简洁的方法去做,将上面的式子回代,
g = x y 1 − x y ⋅ 1 1 − x ⋅ 1 1 − y = ( ∑ i = 1 ∞ x i y i ) ( ∑ i = 1 ∞ x i ) ( ∑ i = 1 ∞ y i ) g=\frac{xy}{1-xy}\cdot \frac{1}{1-x}\cdot \frac{1}{1-y}=(\sum_{i=1}^{\infty}x^iy^i)(\sum_{i=1}^{\infty}x^i)(\sum_{i=1}^{\infty}y^i) g=1−xyxy​⋅1−x1​⋅1−y1​=(i=1∑∞​xiyi)(i=1∑∞​xi)(i=1∑∞​yi)
我们的答案,
f k ( n , m ) = [ x n y m ] g k = [ x n y m ] ( ∑ i = 1 ∞ x i y i ) k ( ∑ i = 0 ∞ x i ) k ( ∑ i = 0 ∞ y i ) k f_k(n,m)=[x^ny^m]g^k=[x^ny^m](\sum_{i=1}^{\infty}x^iy^i)^k(\sum_{i=0}^{\infty}x^i)^k(\sum_{i=0}^{\infty}y^i)^k fk​(n,m)=[xnym]gk=[xnym](i=1∑∞​xiyi)k(i=0∑∞​xi)k(i=0∑∞​yi)k
上面的式子明显可以用组合求出答案,我们可以枚举第一个 ( ∑ i = 1 ∞ x i y i ) k (\sum_{i=1}^{\infty}x^iy^i)^k (∑i=1∞​xiyi)k对我们的次数产生的贡献,显然有,
f k ( n , m ) = ∑ i = k min ⁡ ( n , m ) ( i − 1 k − 1 ) ( n − i + k − 1 k − 1 ) ( m − i + k − 1 k − 1 ) f_k(n,m)=\sum_{i=k}^{\min(n,m)}\binom{i-1}{k-1}\binom{n-i+k-1}{k-1}\binom{m-i+k-1}{k-1} fk​(n,m)=i=k∑min(n,m)​(k−1i−1​)(k−1n−i+k−1​)(k−1m−i+k−1​)
这样,对于单个询问,我们的时间复杂度就是 O ( min ⁡ ( n , m ) − k ) O\left(\min(n,m)-k\right) O(min(n,m)−k)的了。

总时间复杂度 O ( T n ) O\left(Tn\right) O(Tn)。

源码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 1000005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
const LL INF=0x3f3f3f3f3f3f3f3f;
const int mo=998244353;
const int inv2=499122177;
const int jzm=2333;
const int zero=10000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-5;
typedef pair<int,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int T,n,m,k,fac[MAXN],inv[MAXN],f[MAXN],ans;
void init(){
	fac[0]=fac[1]=inv[0]=inv[1]=f[1]=1;
	for(int i=2;i<=1e6;i++){
		fac[i]=1ll*i*fac[i-1]%mo;
		f[i]=1ll*(mo-mo/i)*f[mo%i]%mo;
		inv[i]=1ll*inv[i-1]*f[i]%mo;
	}
}
int C(int x,int y){
	if(x<0||y<0||x<y)return 0;
	return 1ll*fac[x]*inv[y]%mo*inv[x-y]%mo;
}
signed main(){
	//freopen("easy.in","r",stdin);
	//freopen("easy.out","w",stdout);
	init();read(T);
	while(T--){
		read(n);read(m);read(k);ans=0;
		for(int i=k;i<=min(n,m);i++)
			ans=add(ans,1ll*C(i-1,k-1)*C(n-i+k-1,k-1)%mo*C(m-i+k-1,k-1)%mo,mo);
		printf("%d\n",ans);
	}
	return 0;
}

谢谢!!!

posted @ 2021-09-07 16:05  StaroForgin  阅读(42)  评论(0)    收藏  举报  来源