Wannafly挑战赛24 D 无限手套

8 篇文章 0 订阅

R e s u l t Result Result

...


H y p e r l i n k Hyperlink Hyperlink

https://ac.nowcoder.com/acm/contest/186/D


D e s c r i p t i o n Description Description

一个无限手套,有 n n n个槽,第 i i i个槽有属性 a i , b i a_i,b_i ai,bi
若手套第 i i i个槽装上了 x x x颗宝石,这个槽会产生 ( a i x 2 + b i x + 1 ) (a_ix^2+b_ix+1) (aix2+bix+1)的能量
无限手套的能量是它每个槽上面的能量的积

q q q个询问,每个询问给定一个 m m m,询问如果有 m m m个宝石,所有情况产生的能量的和是多少?
答案对998244353取模

数据范围: n ≤ 1 0 3 , m ≤ 1 0 4 n\leq 10^3,m\leq 10^4 n103,m104


S o l u t i o n Solution Solution

据说有生成函数的做法,但是我不会【我太菜了】
f i , j f_{i,j} fi,j表示处理到第 i i i个槽, j j j颗宝石能产生的能量之和

则我们显然是枚举上一个槽选了多少个宝石 k k k,有转移
f i , j = ∑ k = 0 j f i − 1 , j × y ( j − k ) f_{i,j}=\sum_{k=0}^j f_{i-1,j}\times y(j-k) fi,j=k=0jfi1,j×y(jk) y ( x ) y(x) y(x)表示在第 i i i个槽下装 x x x个宝石会产生的能量

直接这样做时间复杂度为 O ( n m 2 ) O(nm^2) O(nm2)显然会 T T T,我们考虑优化它

观察 f i , j f_{i,j} fi,j的式子可以发现,实际上它和上一次枚举的 f i , j − 1 f_{i,j-1} fi,j1只有两个地方不同,我们写出来看一下
f i , j = ∑ k = 0 j f i − 1 , j × y ( j − k ) f_{i,j}=\sum_{k=0}^j f_{i-1,j}\times y(j-k) fi,j=k=0jfi1,j×y(jk)
f i , j − 1 = ∑ k = 0 j − 1 f i − 1 , j × y ( j − k − 1 ) f_{i,j-1}=\sum_{k=0}^{j-1} f_{i-1,j}\times y(j-k-1) fi,j1=k=0j1fi1,j×y(jk1)

f i , j f_{i,j} fi,j在求和相差的那一部分多枚举了一个 k = j k=j k=j的情况
这种情况的差值显然就是
A = f i − 1 , j × y ( j − k ) = f i − 1 , j A=f_{i-1,j}\times y(j-k)=f_{i-1,j} A=fi1,j×y(jk)=fi1,j

考虑计算求和中间那一段的差值,试着上下相减,看一下每一项的差值
f i − 1 , j × y ( j − k ) − f i − 1 , j × y ( j − k − 1 ) f_{i-1,j}\times y(j-k) -f_{i-1,j}\times y(j-k-1) fi1,j×y(jk)fi1,j×y(jk1)

分配率
f i − 1 , j × [ y ( j − k ) − y ( j − k − 1 ) ] f_{i-1,j}\times[y(j-k)-y(j-k-1)] fi1,j×[y(jk)y(jk1)]

y y y拆开
f i − 1 , j × [ a i ( j − k ) 2 + b i ( j − k ) − a i ( j − k − 1 ) 2 − b i ( j − k − 1 ) ] f_{i-1,j}\times[a_i(j-k)^2+b_i(j-k)-a_i(j-k-1)^2-b_i(j-k-1)] fi1,j×[ai(jk)2+bi(jk)ai(jk1)2bi(jk1)](常数项显然抵消掉了)
x 2 − ( x − 1 ) 2 = 2 x − 1 x^2-(x-1)^2=2x-1 x2(x1)2=2x1,可以得到

f i − 1 , j × [ a i ( 2 j − 2 k − 1 ) + b i ] f_{i-1,j}\times[a_i(2j-2k-1)+b_i] fi1,j×[ai(2j2k1)+bi]

考虑把与 j j j相关的写一起,与 k k k相关的写一起
得到了第二部分的差值
B = f i − 1 , j × [ b i + ( 2 j − 1 ) a i − 2 k a i ] B=f_{i-1,j}\times[b_i+(2j-1)a_i-2ka_i] B=fi1,j×[bi+(2j1)ai2kai]

b i , a i b_i,a_i bi,ai是常量,换言之这些是我们已知的
也就是每一个 f i , j f_{i,j} fi,j都和 f i , j − 1 f_{i,j-1} fi,j1差了 A + B A+B A+B

A A A显然很好求,考虑求 B B B

对于每一个 k k k,都会有 f i − 1 , j [ b i + ( 2 j − 1 ) a i − 2 k a i ] f_{i-1,j}[b_i+(2j-1)a_i-2ka_i] fi1,j[bi+(2j1)ai2kai]的差值

观察到我们始终只关心 f i f_i fi f i − 1 f_i-1 fi1,为了方便我们把它们分别设成 f f f g g g【这次的和上一次的】
B = g j [ b i + ( 2 j − 1 ) a i − 2 k a i ] B=g_j[b_i+(2j-1)a_i-2ka_i] B=gj[bi+(2j1)ai2kai]
拆成两坨 B = g j [ b i + ( 2 j − 1 ) a i ] − g i × 2 k a i B=g_j[b_i+(2j-1)a_i]-g_i\times 2ka_i B=gj[bi+(2j1)ai]gi×2kai
记录前缀和 s j = ∑ k = 0 j g j , S k = ∑ j = 0 k g j × 2 j s_j=\sum_{k=0}^j g_j,S_k=\sum_{j=0}^k g_j\times 2j sj=k=0jgj,Sk=j=0kgj×2j

这样就可以从 f j − 1 f_{j-1} fj1快速的转移到 f j f_j fj了,因为 f j f_j fj f j − 1 f_{j-1} fj1的差值就是 A + B A+B A+B

f j = f j − 1 + A + B f_j=f_{j-1}+A+B fj=fj1+A+B,这是笼统的总式子,接下来我们把它写细一点
首先 A A A是以前的 f i − 1 , j f_{i-1,j} fi1,j,现在换了 d p dp dp后是 g j g_j gj
B B B可以表示成两段前缀和,即 s j − 1 × ( b i + ( 2 j − 1 ) a i ) − S j − 1 × a i s_{j-1}\times (b_i+(2j-1)a_i)-S_{j-1}\times a_i sj1×(bi+(2j1)ai)Sj1×ai【之所以是 j − 1 j-1 j1是因为 f j − 1 f_j-1 fj1只计算到 j − 1 j-1 j1,所以这一部分的差值应该是到 j − 1 j-1 j1

写成真正的 d p dp dp转移方程
f j = f j − 1 + g j + s j − 1 × ( b i + ( 2 j − 1 ) a i ) − S j − 1 × a i f_j=f_{j-1}+g_j+s_{j-1}\times (b_i+(2j-1)a_i)-S_{j-1}\times a_i fj=fj1+gj+sj1×(bi+(2j1)ai)Sj1×ai【前面有一个 f j − 1 f_{j-1} fj1是因为我们后面算的是和上一次的差值】
有点丑,将常量 b i + ( 2 j − 1 ) a i b_i+(2j-1)a_i bi+(2j1)ai用变量 t m p tmp tmp表示起来,就舒服多了

f j = f j − 1 + g j + s j − 1 × t m p − S j − 1 × a i f_j=f_{j-1}+g_j+s_{j-1}\times tmp-S_{j-1}\times a_i fj=fj1+gj+sj1×tmpSj1×ai

最后答案就是 f m f_m fm,时间复杂度: O ( n m ) O(nm) O(nm)


C o d e Code Code
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define N 10001
#define mod 998244353
using namespace std;int n,q;
LL a,b,f[N],g[N],s[N],S[N],tmp;
inline LL read()
{
	char c;LL d=1,f=0;
	while(c=getchar(),!isdigit(c)) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
	while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
	return d*f;
}
signed main()
{
	f[0]=g[0]=s[0]=1;n=read();
	for(register int i=1;i<=n;i++)
	{
		a=read();b=read();
		for(register int j=1;j<N;j++) (s[j]=s[j-1]+g[j])%=mod,(S[j]=S[j-1]+2*g[j]*j%mod)%=mod;
		for(register int j=1;j<N;j++) tmp=(b+(2*j-1)*a%mod)%mod,f[j]=(f[j-1]+g[j]+(s[j-1]*tmp%mod-S[j-1]*a%mod+mod)%mod)%mod;
		for(register int j=1;j<N;j++) g[j]=f[j];
	}
	q=read();
	while(q--) printf("%lld\n",f[read()]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值