LOJ3045 「ZJOI2019」开关

https://loj.ac/problem/3045
简单的题目描述往往有着不简单的解法…

要点分析

考虑一个EGF
在这里插入图片描述
第i项表示长度为i的最终状态为s的序列有多少个。

在这里插入图片描述
再考虑上面的EGF,第i项是每一位操作了偶数次的序列有多少个。

这两个都可以递推出来,只需要知道存 e v x e^{vx} evx的系数就可以了。

考虑如何把EGF转OGF:
在这里插入图片描述
设f,g分别是F,G对应的ogf.
设h是答案ogf,则有 h ( x ) g ( x ) = f ( x ) h(x)g(x)=f(x) h(x)g(x)=f(x)

现在答案为 h ′ ( 1 ) h'(1) h(1),即为求 [ f ( 1 ) g ( 1 ) ] ′ = f ′ ( 1 ) g ( 1 ) − f ( 1 ) g ′ ( 1 ) g 2 ( 1 ) [\frac {f(1)} {g(1)}]'=\frac{f'(1)g(1)-f(1)g'(1)}{g^2(1)} [g(1)f(1)]=g2(1)f(1)g(1)f(1)g(1)
但是f,g作为形式幂级数,发现把x=1带进去存在分母=0的情况。对形式幂级数一发乱搞通分,将分母移动到分子,然后上下可以把分母约去。

然后原函数值随便算,导函数的值看起来难算但实际上有那个为0的原先分母项的都不用考虑。所以直接线性或者带个log都可以。

不知道为什么我算出来就是答案的相反数…找不到原因。

几个新操作:

  1. 生成函数的题,答案不一定是某项系数。这题就是直接求生成函数(的导函数)值。
  2. 已知EGF求OGF的,求出egf的e^vx系数就可以实现。
  3. 形式幂级数中的x事实上只是一个记号。讲究有没有意义、收不收敛本身就没有意义。只要能带进去算值,就可以得出期望的结果。
  4. 答案 * 恒等 = 好算的类答案 (意会一下)
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define inv(x) ksm((x) % mo, mo-2)
using namespace std;
typedef long long ll;
const int N = 110, M = 100010 * 2, mo = 998244353;
int n, s[N];
ll P,p[N];
ll f[M], g[M], tmp[M];
ll Z = 50010;
ll sum, ny;

ll ksm(ll x,ll y) {
	ll ret = 1; for(; y; y>>=1) {
		if (y & 1) ret = ret * x % mo;
		x = x * x % mo;
	}
	return ret;
}

ll val(ll *u) {
	ll ret = u[Z + sum];
	for(int i = - sum; i < sum; i++)
		ret = ret * (1 - i * ny % mo) % mo;
	return ret;
}

ll dao(ll *u) {
	ll s = 1;
	for(int i = - sum; i < sum; i++)
		s = s * (1 - i * ny % mo) % mo;
	ll ret = 0;
	for(int i = - sum; i < sum; i++) {
		ret = (ret + u[Z + i] * s % mo * inv(1 - i * ny) % mo) % mo;
	}
	for(int i = - sum; i < sum; i++) {
		ret = (ret + u[Z + sum] * s % mo * inv(1 - i * ny) % mo) % mo;
	}
	return ret;
}

int main(){
	freopen("a.in","r",stdin);
	cin>>n;
	for(int i = 1; i <= n; i++) scanf("%d",&s[i]);
	for(int i = 1; i <= n; i++) scanf("%lld",&p[i]), sum+=p[i];
	ny = inv(sum);
	f[Z] = g[Z] = 1;
	for(int i = 1; i <= n; i++) {
		memset(tmp,0,sizeof tmp);
		for(int j = Z - sum; j <= Z + sum; j++) {
			tmp[j] = (j - p[i] >= 0 ? f[j - p[i]] : 0) + (s[i] ? -1 : 1) * f[j + p[i]];
			tmp[j] %= mo;
		}
		memcpy(f, tmp, sizeof tmp);
		
		memset(tmp,0,sizeof tmp);
		for(int j = Z - sum; j <= Z + sum; j++) {
			tmp[j] = (j - p[i] >= 0 ? g[j - p[i]] : 0) + g[j + p[i]];
			tmp[j] %= mo;
		}
		memcpy(g, tmp, sizeof tmp);
	}
	ll ans = (dao(f) * val(g) % mo - dao(g) * val(f) % mo) % mo;
	ans = ans * inv(val(g) * val(g)) % mo;
	printf("%lld\n",((mo-ans)%mo+mo)%mo);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值