[ZJOI2019]开关

开关

题解

简单dp。虽然是用生成函数想的。

我们将F\left(x \right )=1+x+x^{2}+...的每一项的系数定为按i次达到目标状态的次数。

显然,F\left(x \right )=\frac{\prod_{i=1}^{n} e^{xp_{i}}+(-1)^{s_{i}}e^{-xp_{i}}}{2},这里的p_{i}=\frac{p_{i}}{\sum_{j=1}^{n} p_{j}}

可我们却发现里面有一些重复的次数,所以我们需要将这些重复的去掉。

我们用G\left(x \right )表示扣除掉第一次后回到原点的次数,那么

G\left(x \right )=\frac{\prod_{i=1}^{n}e^{xp_{i}}+e^{-xp_{i}}}{2}

对于这个式子,我们只需要记录F\left(x \right )=\sum a_{i}e^{ix}a_{i}的系数即可。

我们令答案的生成函数为H\left(x \right ),那么显然f\left(x \right )=g\left(x \right )h\left(x \right )

显然,F\left(x \right )=a_{i}e^{ix}\rightarrow f\left(x \right )= \sum \frac{a_{i}}{1-ix},之后就可以用除法求导公式求出h'\left(x \right )

忽然发现可以用dp解决了:

g_{j}= g_{j-a_{i}}+g_{j+a_{i}}

dp_{j}= dp_{j-a_{i}}+(-1)^{s_{i}}dp_{j+a_{i}}

那么答案即为sum\cdot \sum_{i=-sum}^{sum} \frac{g_{j}-dp_{j}}{sum-j} ,(sum= \sum a_{i})

如此,dp就可以解决这道题了。挺简单的,dp的板题

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
using namespace std;
#define MAXN 200005
typedef long long LL;
#define int LL
const int mo=998244353;
const int zero=1e5;
const int G=3;
#define gc() getchar()
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=gc();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=gc();}
	while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=gc();}
	x*=f;
}
int qkpow(int a,int s){
	int t=1;
	while(s){
		if(s&1) t=t*a%mo;
		a=a*a%mo;s>>=1; 
	}
	return t;
}
int n,a[MAXN],s[MAXN],sum,ans;
int dp[MAXN],g[MAXN],tmp[MAXN];
signed main(){ 
	read(n);
	for(int i=1;i<=n;i++)read(s[i]);
	for(int i=1;i<=n;i++)read(a[i]);
	dp[zero]=g[zero]=1;
	for(int i=1;i<=n;i++){
		sum+=a[i];
		for(int j=-sum;j<=sum;j++)tmp[j+zero]=(g[j-a[i]+zero]+g[j+a[i]+zero])%mo;
		for(int j=zero-sum;j<=zero+sum;j++)g[j]=tmp[j];
		for(int j=-sum;j<=sum;j++)tmp[j+zero]=(dp[j-a[i]+zero]+(s[i]?mo-dp[j+a[i]+zero]:dp[j+a[i]+zero]))%mo;
		for(int j=zero-sum;j<=zero+sum;j++)dp[j]=tmp[j];
	}
	for(int i=-sum;i<sum;i++)ans=(ans+(g[i+zero]-dp[i+zero]+mo)%mo*qkpow(sum-i,mo-2)%mo)%mo;
	printf("%lld",ans*sum%mo);
    return 0;
}

谢谢!!!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值