【GDOI2019Day1模拟2019.4.28】星际穿越(简单容斥+EGF+多项式求逆)

Description:

在这里插入图片描述

题解:

不难发现当r=1时就是选若干上升序列。

而一列至少有一行不满足就不稳定的。

这个破限制肯定要想方设法用容斥搞掉。

至少一行不满足太难算了,不妨改成算全部都满足,假设有i个列需要不稳定,即0列全部满足,但是有j个列全部满足,即稳定,则容斥系数为 ( − 1 ) j (-1)^{j} (1)j

这样就可以得到一个显然的dp,先把整个序列每k个分块,最后一块不完整先不管。
f [ i ] [ j ] f[i][j] f[i][j]表示前i块,选了j个上升序列,ps.那么有i-j个全部满足的列
注意用EGF去搞上升序列的选择。
f [ i ] [ j ] = ∑ u = 0 i − 1 f [ u ] [ j − 1 ] ∗ ( 1 / ( ( i − u ) k ) ! ) r f[i][j]=\sum_{u=0}^{i-1}f[u][j-1]*{(1/((i-u)k)!)^r} f[i][j]=u=0i1f[u][j1](1/((iu)k)!)r
A n s = n ! ∗ ∑ i = 0 m ∑ j = 0 m f [ i ] [ j ] ∗ ( ( n − i k ) ! ) r ∗ ( − 1 ) m − j ∗ ( − 1 ) m Ans=n!*\sum_{i=0}^m \sum_{j=0}^m f[i][j]*((n-ik)!)^r*(-1)^{m-j}*(-1)^m Ans=n!i=0mj=0mf[i][j]((nik)!)r(1)mj(1)m
这里 m = ( n − 1 ) / k m=(n-1)/k m=(n1)/k

发现j这一维其实没有用,因为转移时每次j就是+1,那么对最终统计的贡献就是*-1

f [ i ] = − ∑ u = 0 i − 1 f [ u ] ∗ ( 1 / ( ( i − u ) k ) ! ) r f[i]=-\sum_{u=0}^{i-1}f[u]*{(1/((i-u)k)!)^r} f[i]=u=0i1f[u](1/((iu)k)!)r
A n s = n ! ∗ ∑ i = 0 m f [ i ] ∗ ( ( n − i k ) ! ) r ∗ ( − 1 ) i ∗ ( − 1 ) m − i Ans=n!*\sum_{i=0}^mf[i]*((n-ik)!)^r*(-1)^i*(-1)^{m-i} Ans=n!i=0mf[i]((nik)!)r(1)i(1)mi
A n s = n ! ∗ ∑ i = 0 m f [ i ] ∗ ( ( n − i k ) ! ) r ∗ ( − 1 ) m Ans=n!*\sum_{i=0}^mf[i]*((n-ik)!)^r*(-1)^m Ans=n!i=0mf[i]((nik)!)r(1)m

观察f的转移:
f [ i ] = − ∑ u = 0 i − 1 f [ u ] ∗ ( 1 / ( ( i − u ) k ) ! ) r f[i]=-\sum_{u=0}^{i-1}f[u]*{(1/((i-u)k)!)^r} f[i]=u=0i1f[u](1/((iu)k)!)r
不难得到分治NTT的做法,但是显然这可以求逆:
G [ i > 0 ] = ( 1 / ( i k ) ! ) r G[i>0]=(1/(ik)!)^r G[i>0]=(1/(ik)!)r,则 F = G ∗ F + 1 F=G*F+1 F=GF+1
F = 1 / ( 1 − G ) F=1/(1-G) F=1/(1G)

Code:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
#define ff(i, x, y) for(int i = x, B = y; i <  B; i ++)
#define fd(i, x, y) for(int i = x, B = y; i >= B; i --)
#define pp printf
#define ll long long
using namespace std;

const int mo = 998244353;

ll ksm(ll x, ll y) {
	ll s = 1;
	for(; y; y /= 2, x = x * x % mo)
		if(y & 1) s = s * x % mo;
	return s;
}

const int N = 2097152;

ll a[N]; int r[N];
void dft(ll *a, int n, int F) {
	ff(i, 0, n) {
		r[i] = r[i / 2] / 2 + (i & 1) * n / 2;
		if(i < r[i]) swap(a[i], a[r[i]]);
	}
	for(int h = 1; h < n; h *= 2) {
		ll wn = ksm(ksm(3, (mo - 1) / 2 / h), F == 1 ? 1 : mo - 2);
		for(int j = 0; j < n; j += 2 * h) {
			ll A, *l = a + j, *r = a + j + h, w = 1;
			ff(i, 0, h) {
				A = *r * w, *r = (*l - A) % mo, *l = (*l + A) % mo;
				w = w * wn % mo, l ++, r ++;
			}
		}
	}
	if(F == -1) {
		ll v = ksm(n, mo - 2);
		ff(i, 0, n) a[i] = (a[i] + mo) * v % mo;
	}
}
typedef vector<ll> V;
#define si size()
void dft(V &p, int F) {
	ff(i, 0, p.si) a[i] = p[i];
	dft(a, p.si, F);
	ff(i, 0, p.si) p[i] = a[i];
}
V qni(V a) {
	int n0 = 1; while(n0 <= a.si) n0 *= 2;
	V b; b.resize(1); b[0] = ksm(a[0], mo - 2);
	for(int n = 2; n <= n0; n *= 2) {
		V c = a, d = b; c.resize(n);
		c.resize(2 * n); dft(c, 1);
		b.resize(2 * n); dft(b, 1);
		ff(i, 0, 2 * n) b[i] = b[i] * b[i] % mo * c[i] % mo;
		dft(b, -1); b.resize(n); d.resize(n);
		ff(i, 0, n) b[i] = (2 * d[i] - b[i] + mo) % mo;
	}
	b.resize(a.si); return b;
}

int n, R, k, m;
ll fac[N], ans;
vector<ll> g;

int main() {
	freopen("interstellar.in", "r", stdin);
	freopen("interstellar.out", "w", stdout);
	scanf("%d %d %d", &n, &R, &k);
	fac[0] = 1; fo(i, 1, n) fac[i] = fac[i - 1] * i % mo;
	ll fn = fac[n];
	fo(i, 0, n) fac[i] = ksm(fac[i], mo - 2);
	m = (n - 1) / k;
	g.resize(m + 1);
	fo(i, 0, m) g[i] = ksm(fac[i * k], R);
	g[0] = 1; g = qni(g);
	fo(i, 0, m) ans += g[i] * ksm(fac[n - i * k], R) % mo;
	ans = (ans % mo * ((m & 1) ? -1 : 1) + mo) * ksm(fn, R) % mo;
	pp("%lld\n", ans);
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值