【集训队作业2018】矩形
tag:推柿子
又到了最喜欢毒瘤的推柿子题
首先dp式子很容易
显然\(p\alpha+q\beta\)这部分可以提出来单独计算,然后可以枚举每一个\(a_i,b_i\),直接计算它们对答案的贡献(a和b的计算类似)
Case 1
考虑\(b_i\)的贡献,先提出\(b_ih^i\),设\(A=ph^{n+1},B=qh\),则贡献类似于
\(ans=b_xh^xA\cdot\sum_{i=0}^{n-1}\sum_{j=0}^{n-x}A^iB^j\binom{i+j}i\),设后面一堆为\(f(x)\)
然后继续设后面一堆为\(g(j)\),则\(f(n)=f(n-1)+B^ng(n)\)
然后好像推不动了,式子里面看上去能动的就只有组合数了,把它拆开
然后\(i=0\)时左边那个式子是无意义的,所以\(i\in[1,k-1]\),再代换一下令\(i'=i-1\)
容易发现右边是\(g(n-1)\),右边是\(g(n)-(i=k-1)\)的那项
然后对\(A=1\)分类讨论一下即可求出\(g\),然后推出\(f\)(\(a_i\)的话其实把\(A\)和\(B\)swap一下重新跑一遍就好,然后注意提出来的是\(a_xh^{x(n+1)}\))
Case 2
令\(c=p\alpha+q\beta\),考虑\(c\)的贡献
枚举一个格子,再枚举它左上角的一个格子,这两个格子之间的每条路径都会产生\(cp^iq^j\)的贡献(\(ij\)为\(xy\)坐标之差)
设\(h^y\)后面一堆为\(G(x,y)\)
老套路把组合数拆了
第一个式子在\(j=0\)时无意义,第二个在\(i=0\)时无意义,但是这样会忽略掉\(i=j=0\)的情况,所以单独加上
注意到右边是\(pG(x-1,y)\),左边是\(qG(x,y)减去j=y-1\)的情况
考虑我们要求的实际上是\(\sum_{x,y}G(x,y)\),所以设\(T(x)=\sum_{i=1}^nG(x,i)\)
中间那个式子换一下下标
然后发现,它(右边部分提一个\(h\))居然长得和\(g(i)\)一样!
所以令\(A=qh\)求一遍\(g\)即可(实际上就是求\(a_i\)的时候的\(g\))
对\(q=1\)分类讨论一下可以求出\(T\)
(上述所有带和式的都可以前缀和优化)
于是复杂度\(O(n)\)(可能多一个快速幂的复杂度)
#include<bits/stdc++.h>
using namespace std;
template<typename T>
inline void Read(T &n){
char ch; bool flag=false;
while(!isdigit(ch=getchar()))if(ch=='-')flag=true;
for(n=ch^48;isdigit(ch=getchar());n=(n<<1)+(n<<3)+(ch^48));
if(flag)n=-n;
}
typedef long long ll;
const int MAXN = 200005;
const int MOD = 998244353;
inline int inc(int a, int b){
a += b;
if(a>=MOD) a -= MOD;
return a;
}
inline int dec(int a, int b){
a -= b;
if(a<0) a += MOD;
return a;
}
inline int ksm(int base, ll k=MOD-2){
int res=1;
while(k){
if(k&1)
res = 1ll*res*base%MOD;
base = 1ll*base*base%MOD;
k >>= 1;
}
return res;
}
int jc[MAXN<<1], invjc[MAXN<<1];
inline int C(int n, int m){return 1ll*jc[n]*invjc[m]%MOD*invjc[n-m]%MOD;}
int a[MAXN], b[MAXN], alpha, beta, n, h, p, q;
int S1[MAXN], S2[MAXN], ans, A, B;
int f[MAXN], g[MAXN], sg[MAXN], T[MAXN];
int main(){
// freopen("dream2.in","r",stdin);
Read(n); Read(h); Read(alpha); Read(beta);
jc[0] = 1; for(register int i=1; i<=(n<<1); i++) jc[i] = 1ll*jc[i-1]*i%MOD;
invjc[n<<1] = ksm(jc[n<<1]); for(register int i=(n<<1); i; i--) invjc[i-1] = 1ll*invjc[i]*i%MOD;
int tmp; Read(p); Read(tmp); p = 1ll*p*ksm(tmp)%MOD;
Read(q); Read(tmp); q = 1ll*q*ksm(tmp)%MOD;
for(register int i=1; i<=n; i++) Read(a[i]);
for(register int i=1; i<=n; i++) Read(b[i]);
// bi的贡献
for(register int i=1; i<=n; i++) ans = (ans+1ll*b[i]*ksm(h,i))%MOD;
A = 1ll*p*ksm(h,n+1)%MOD; B = 1ll*q*h%MOD;
if(A==1) for(register int i=0; i<n; i++) g[i] = C(n+i,n-1);
else{
for(register int i=0; i<n; i++) g[0] = inc(g[0],ksm(A,i));
for(register int i=1; i<n; i++) g[i] = 1ll*dec(g[i-1],1ll*C(i+n-1,n-1)*ksm(A,n)%MOD)*ksm(dec(1,A))%MOD;
}
f[0] = g[0]; for(register int i=1; i<n; i++) f[i] = (f[i-1]+1ll*ksm(B,i)*g[i])%MOD;
for(register int i=1; i<=n; i++) ans = (ans+1ll*b[i]*ksm(h,i)%MOD*A%MOD*f[n-i])%MOD;
// ai的贡献
for(register int i=1; i<=n; i++) ans = (ans+1ll*a[i]*ksm(h,1ll*i*(n+1)))%MOD;
swap(A,B); memset(g,0,sizeof g);
if(A==1) for(register int i=0; i<n; i++) g[i] = C(n+i,n-1);
else{
for(register int i=0; i<n; i++) g[0] = inc(g[0],ksm(A,i));
for(register int i=1; i<n; i++) g[i] = 1ll*dec(g[i-1],1ll*C(i+n-1,n-1)*ksm(A,n)%MOD)*ksm(dec(1,A))%MOD;
}
f[0] = g[0]; for(register int i=1; i<n; i++) f[i] = (f[i-1]+1ll*ksm(B,i)*g[i])%MOD;
for(register int i=1; i<=n; i++) ans = (ans+1ll*a[i]*ksm(h,1ll*i*(n+1))%MOD*A%MOD*f[n-i])%MOD;
// c的贡献
int c = (1ll*alpha*p+1ll*beta*q)%MOD;
int spow=0; for(register int i=1, pw=h; i<=n; i++, pw=1ll*pw*h%MOD) spow = inc(spow,pw);
if(q==1) for(register int i=1; i<=n; i++) T[i] = (1ll*p*T[i-1]+spow)%MOD;
else{
sg[0] = g[0];
for(register int i=1; i<=n; i++)
sg[i] = (sg[i-1]+1ll*ksm(p,i)*g[i])%MOD,
T[i] = 1ll*ksm(dec(1,q))*((1ll*p*T[i-1]%MOD-1ll*q*h%MOD*sg[i-1]%MOD+spow+MOD)%MOD)%MOD;
}
for(register int i=1; i<=n; i++) ans = (ans+1ll*c*T[i]%MOD*ksm(h,1ll*i*(n+1)))%MOD;
cout<<ans<<endl;
return 0;
}