BNUOJ51279 組隊活動(cdq分治&&NTT)

題意:N個人進行組隊,每個隊不超過M人,求方案數模998244353。

這個遞推方程我都沒想到。。枚舉當前這個人所在隊伍剩餘人數j,則f[i]=Σf[i-1-j]*C(i-1, j),把組合數展開後發現是個卷積的形式,但是不能直接NTT,因爲之前的f值沒有求出來。套用cash一題的策略使用cdq分治解決,先遞歸左邊,再用左邊的更新右邊,再遞歸右邊。注意NTT需要補成2的次冪,常數有點大,但只要保證每次多項式的長度不超過2*(R-L+1)就不會影響時間複雜度。

這樣看來NTT和cdq分治挺容易結合的。。只有出題人稍微構造一下遞推式就可以造一道毒瘤題。。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define erp(i,a,b) for(int i=a;i>=b;--i)
#define LL long long
using namespace std;
const int MAXN = 100010, MAXL = 270000;
const int mo = 998244353, g = 3, T = 262144;
inline void add(int&a, int b) { a+=b; if(a>=mo) a-=mo; }

LL ksm(LL a, LL b)
{
	LL r = 1;
	for (; b>0; b>>=1, a=a*a%mo)
		if (b&1) r = r*a%mo;
	return r;
}

int jc[MAXL], rjc[MAXL], inv[MAXL];
void makejc()
{
	jc[0] = rjc[0] = inv[0] = 1;
	jc[1] = rjc[1] = inv[1] = 1;
	rep(i, 2, T)
	{
		jc[i] = 1ll * i * jc[i-1] % mo;
		inv[i] = 1ll * (mo-mo/i) * inv[mo%i] % mo;
		rjc[i] = 1ll * rjc[i-1] * inv[i] % mo;
	}
}

namespace NTT
{
	int r[MAXL], L;
	int a[MAXL], b[MAXL], wn[MAXL], invwn[MAXL], invn;
	void init()
	{
		LL r = ksm(g, (mo-1)/T), invr = ksm(r, mo-2);
		wn[0] = invwn[0] = 1;
		rep(i, 1, T-1)
		{
			wn[i] = 1ll*wn[i-1]*r%mo;
			invwn[i] = 1ll*invwn[i-1]*invr%mo;
		}
	}
	void ntt(int*a, int flag, int N)
	{
		int *w = (flag==1?wn:invwn);
		rep(i, 0, N-1) if (i<r[i]) swap(a[i], a[r[i]]);
		for (int i = 1; i<N; i<<=1)
			for (int j = 0; j<N; j+=i<<1)
				for (int k = 0; k<i; ++k)
				{
					int x = a[j+k], y = 1ll*w[T/(i<<1)*k]*a[j+k+i]%mo;
					a[j+k] = (x+y) % mo;
					a[j+k+i] = (x+mo-y) % mo;
				}
		if (flag<0)
		{
			invn = ksm(N, mo-2);
			rep(i, 0, N-1) a[i] = 1ll*a[i]*invn%mo;
		}
	}
	void mul(int*pol1, int*pol2, int N, int M)
	{
		rep(i, 0, N) a[i] = pol1[i];
		rep(i, 0, M) b[i] = pol2[i];
		int tn = N+1, tm = M+1; r[0] = 0;
		for (M+=N, L=0, N=1; N<=M; N<<=1) ++L;
		rep(i, tn, N) a[i] = 0;
		rep(i, tm, N) b[i] = 0;
		rep(i, 0, N) r[i] = (r[i>>1]>>1)|((i&1)<<(L-1));
		ntt(a, 1, N); ntt(b, 1, N);
		rep(i, 0, N) a[i] = 1ll*a[i]*b[i]%mo;
		ntt(a, -1, N);
		rep(i, 0, M) pol1[i] = a[i];
	}
}

int p1[MAXL], p2[MAXL], f[MAXN];
int cas, n, m;
void cdq(int L, int R)
{
	if (L==R) return;
	int mid = (L+R)>>1, len = R - L + 1;
	cdq(L, mid); //f[L...mid] is clear
	rep(i, 0, mid-L) p1[i] = 1ll * f[i+L] * rjc[i+L] % mo;
	rep(i, mid-L+1, len*2) p1[i] = 0;
	rep(i, 0, len) p2[i] = i<m ? rjc[i] : 0;
	rep(i, len+1, len*2) p2[i] = 0;
	NTT::mul(p1, p2, len, len);
	rep(i, mid+1, R) add(f[i], 1ll*p1[i-L-1]*jc[i-1]%mo);
	cdq(mid+1, R);
}

int main()
{
	NTT::init();
	makejc();
	scanf("%d", &cas);
	while (cas--)
	{
		scanf("%d%d", &n, &m);
		memset(f, 0, sizeof f);
		f[0] = 1;
		cdq(0, n);
		cout << f[n] << '\n';
	}
	return 0;
}
發佈了98 篇原創文章 · 獲贊 26 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章