题意:
有一个长度为 n n n的序列,序列上每个位置有一个物品,每个物品在每次被询问到的时候有 p i p_i pi的概率被拒绝,考虑这样一个过程:先取前 L L L个物品,然后每次对这 L L L个物品都询问,如果有 x x x个物品被拒绝,则将前 x x x个物品丢弃,然后从未被选取的物品中选取最靠前的 x x x个物品重复这个过程,当总物品数 < L \lt L <L时停止,问期望询问多少组次。
n , L ≤ 1 e 5 n , L \leq 1e5 n,L≤1e5,
m o d 998244353 \bmod 998244353 mod998244353
可以写出 n 2 d p n^2dp n2dp
f i f_i fi表示现在手上的物品是 [ i , i + L − 1 ] [i,i+L-1] [i,i+L−1]时期望还需要多少步。
f i = ∑ j = 1 L f i , j ( [ x j ] ∏ k = i i + L − 1 p k x + ( 1 − p k ) ) 1 − ∏ j = 0 L − 1 ( 1 − p i + j ) f_i = \frac {\sum_{j=1}^L f_{i,j}\left([x^j]\prod_{k=i}^{i+L-1}p_{k}x+(1-p_k)\right)}{1 - \prod_{j=0}^{L-1} (1 - p_{i+j})} fi=1−∏j=0L−1(1−pi+j)∑j=1Lfi,j([xj]∏k=ii+L−1pkx+(1−pk))
下面那个可以简单 O ( n ) O(n) O(n)计算,
上面那个。。。
凭什么这个 d p dp dp要平白无故地处理多个多项式还开 1 e 5 ? 1e5? 1e5?
你可以拿起笔,在草稿纸上涂涂画画,写下一堆形如“ H ( u , x ) = u k ( p k x + ( 1 − p k ) ) H(u,x)=u^k(p_{k}x+(1-p_k)) H(u,x)=uk(pkx+(1−pk))”的东西,然后拍案而起:“不会做,自闭了,我要去洗澡!”
你也可以平心静气,突然发现,唉,上面的多项式长得都差不多啊,然后安静地度过一天。
其实上面那个式子是可以分治 N T T NTT NTT的。
首先差卷积不太爽,我们将 p p p翻转,那么我们实际上也就是把 f f f也翻转了,最后的答案就是 f n f_n fn。
设 F j ( x ) = ∑ i = 1 j f i x i F_j(x) = \sum_{i=1}^j f_ix^i Fj(x)=∑i=1jfixi
则在 j < L j \lt L j<L时 F j ( x ) = 0 F_j(x) = 0 Fj(x)=0
j ≥ L j \geq L j≥L时 F j ( x ) = [ x j ] F j − 1 ( x ) P i ( x ) 1 − ∏ j = 0 L − 1 ( 1 − p i − j ) F_j(x) = \frac {[x^j]F_{j-1}(x)P_i(x)}{1 - \prod_{j=0}^{L-1} (1 - p_{i-j})} Fj(x)=1−∏j=0L−1(1−pi−j)[xj]Fj−1(x)Pi(x)
其中 P i ( x ) = ∏ j = 0 L − 1 p j x + ( 1 − p j ) P_i(x) = \prod_{j=0}^{L-1} p_jx + (1-p_j) Pi(x)=∏j=0L−1pjx+(1−pj)
然后考虑自顶向下的分治 N T T NTT NTT。
当前我们在 [ l , r ] [l,r] [l,r],拥有
A l , r ( x ) = F l − 1 × ∏ r − L + 1 ≤ i ≤ l p i x + 1 − p i A_{l,r}(x) = F_{l-1} \times \prod_{r-L+1 \leq i \leq l} p_ix + 1-p_i Al,r(x)=Fl−1×r−L+1≤i≤l∏pix+1−pi
和
B l , r ( x ) = ∏ r − L + 1 ≤ i ≤ l p i x + 1 − p i B_{l,r}(x) = \prod_{r-L+1 \leq i \leq l} p_ix + 1-p_i Bl,r(x)=r−L+1≤i≤l∏pix+1−pi
那么可以发现如果 l = r l = r l=r,则 A l , l ( x ) = F l − 1 ( x ) × P l ( x ) A_{l,l}(x) = F_{l-1}(x) \times P_l(x) Al,l(x)=Fl−1(x)×Pl(x)
就是我们想要的东西,求个第 l l l项即可。
考虑怎么在走到 [ l , m i d ] [l,mid] [l,mid]和 [ m i d + 1 , r ] [mid+1,r] [mid+1,r]的时候快速的维护出 A l , m i d , A m i d + 1 , r , B l , m i d , B m i d + 1 , r A_{l,mid},A_{mid+1,r},B_{l,mid},B_{mid+1,r} Al,mid,Amid+1,r,Bl,mid,Bmid+1,r。
当 r r r变成 m i d mid mid时,可以发现我们可以直接
A l , m i d = A l , r × ∏ i = m i d + 1 r p i − L x + 1 − p i − L A_{l,mid} = A_{l,r}\times \prod_{i=mid+1}^r p_{i-L}x + 1 - p_{i-L} Al,mid=Al,r×∏i=mid+1rpi−Lx+1−pi−L
B l , m i d = B l , r × ∏ i = m i d + 1 r p i − L x + 1 − p i − L B_{l,mid} = B_{l,r}\times \prod_{i=mid+1}^r p_{i-L}x + 1 - p_{i-L} Bl,mid=Bl,r×∏i=mid+1rpi−Lx+1−pi−L
当 l l l变成 m i d + 1 mid+1 mid+1时,我们可以直接
A m i d + 1 , r = A l , r × ∏ i = l + 1 m i d + 1 p i − L x + 1 − p i − L + ( F m i d ( x ) − F l − 1 ( x ) ) × B m i d + 1 , r A_{mid+1,r} = A_{l,r}\times \prod_{i=l+1}^{mid+1} p_{i-L}x + 1 - p_{i-L} + (F_{mid}(x) - F_{l-1}(x)) \times B_{mid+1,r} Amid+1,r=Al,r×∏i=l+1mid+1pi−Lx+1−pi−L+(Fmid(x)−Fl−1(x))×Bmid+1,r
B m i d + 1 , r = B l , r × ∏ i = l + 1 m i d − 1 p i − L x + 1 − p i − L B_{mid+1,r} = B_{l,r}\times \prod_{i=l+1}^{mid-1} p_{i-L}x + 1 - p_{i-L} Bmid+1,r=Bl,r×∏i=l+1mid−1pi−Lx+1−pi−L
把后面的全部分治 N T T NTT NTT预处理出来即可简单转移、
但是当你写出上面的式子的时候你就 n a i v e naive naive了,所谓细节调一年。
上面的式子其实应该是:
A l , m i d = A l , r × ∏ i = m i d + 1 min ( r , l + L ) p i − L x + 1 − p i − L A_{l,mid} = A_{l,r}\times \prod_{i=mid+1}^{\min(r,l+L)} p_{i-L}x + 1 - p_{i-L} Al,mid=Al,r×∏i=mid+1min(r,l+L)pi−Lx+1−pi−L
B l , m i d = B l , r × ∏ i = m i d + 1 min ( r , l + L ) p i − L x + 1 − p i − L B_{l,mid} = B_{l,r}\times \prod_{i=mid+1}^{\min(r,l+L)} p_{i-L}x + 1 - p_{i-L} Bl,mid=Bl,r×∏i=mid+1min(r,l+L)pi−Lx+1−pi−L
A m i d + 1 , r = A l , r × ∏ i = max ( r − L + 1 , l + 1 ) m i d + 1 p i − L x + 1 − p i − L + ( F m i d ( x ) − F l − 1 ( x ) ) × B m i d + 1 , r A_{mid+1,r} = A_{l,r}\times \prod_{i=\max(r-L+1,l+1)}^{mid+1} p_{i-L}x + 1 - p_{i-L} + (F_{mid}(x) - F_{l-1}(x)) \times B_{mid+1,r} Amid+1,r=Al,r×∏i=max(r−L+1,l+1)mid+1pi−Lx+1−pi−L+(Fmid(x)−Fl−1(x))×Bmid+1,r
B m i d + 1 , r = B l , r × ∏ i = max ( r − L + 1 , l + 1 ) m i d − 1 p i − L x + 1 − p i − L B_{mid+1,r} = B_{l,r}\times \prod_{i=\max(r-L+1,l+1)}^{mid-1} p_{i-L}x + 1 - p_{i-L} Bmid+1,r=Bl,r×∏i=max(r−L+1,l+1)mid−1pi−Lx+1−pi−L
然后你仔细思考这个取 max \max max和取 min \min min,
以取 min \min min为例:
当 l + L < r l+L \lt r l+L<r时,
若 l + L < m i d + 1 l+L \lt mid+1 l+L<mid+1则原多项式为 1 1 1。
否则,只有一个 r r r可以使得 m i d + 1 ≤ l + L < r mid +1 \leq l+L \lt r mid+1≤l+L<r。
对于这种情况直接再暴力一个自下而上的分治 N T T NTT NTT再算一遍即可。(但是好像网上其他解法有些可以避开这种情况的高论。)
其他情况可以一个自下而上的分治 N T T NTT NTT搞定。
还有一些细节比如说 A l , r ( x ) A_{l,r}(x) Al,r(x)我们应该只保留次数在 [ x l − ( r − l + 1 ) , x r ] [x^{l-(r-l+1)} , x^r] [xl−(r−l+1),xr]之间的项来保证复杂度正确。
对于 B l , r ( x ) B_{l,r}(x) Bl,r(x)只保留次数在 [ x l , x r + ( r − l + 1 ) ] [x^l,x^{r + (r-l+1)}] [xl,xr+(r−l+1)]之间的项。
A C C o d e \mathcal AC \ Code AC Code
#include
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define maxn 800005
#define Ct const
#define LL long long
#define pii pair
#define vc vector
#define vi vc
#define mod 998244353
#define pb push_back
#define mp make_pair
#define db double
using namespace std;
namespace IO{
char cb[1<<16],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<16,stdin),cs==ct)?0:*cs++)
void read(int &res){
char ch;bool f=0;
for(;!isdigit(ch=getc());) if(ch=='-') f=1;
for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
(f) && (res = -res);
}
}
int Pow(int b,int k){ int r=1;for(;k;k>>=1,b=1ll*b*b%mod) if(k&1) r=1ll*r*b%mod; return r; }
int n,L,p[maxn],f[maxn],*F[maxn],*G[maxn];
int *P[maxn],*P2[maxn];
int Wl,Wl2,w[maxn],lg[maxn],inv[maxn],fac[maxn],invf[maxn];
void init(int n){
for(Wl=1;n>=Wl<<1;Wl<<=1);
int pw = Pow(3 , (mod-1) / (Wl2=Wl<<1));
w[Wl] = inv[0] = inv[1] = fac[0] = fac[1] = invf[0] = invf[1] = 1;
rep(i,Wl+1,Wl2) w[i] = 1ll * w[i-1] * pw % mod;
per(i,Wl-1,1) w[i] = w[i<<1];
rep(i,2,Wl2) inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod,fac[i] = 1ll * fac[i-1] * i % mod,
invf[i] = 1ll * invf[i-1] * inv[i] % mod , lg[i] = lg[i >> 1] + 1;
}
int upd(int x){ return x += x >> 31 & mod; }
void NTT(int *A,int n,int tp){
static int r[maxn]={};
static unsigned long long ar[maxn];
if(tp ^ 1) reverse(A+1,A+n);
rep(i,0,n-1) r[i] = r[i >> 1] >> 1 | (i&1) << lg[n] - 1 , ar[i] = upd(A[r[i]]);
for(int L=1;L<n;L<<=1) for(int s=0,L2=L<<1;s<n;s+=L2) for(int k=s,x=L,t;k<s+L;k++,x++)
t=w[x]*ar[k+L]%mod,ar[k+L]=ar[k]-t+mod,ar[k]+=t;
rep(i,0,n-1) A[i] = ar[i] % mod;
if(tp ^ 1) rep(i,0,n-1) A[i] = 1ll * A[i] * inv[n] % mod;
}
void Mul(int *A,int *B,int *C,int n,int m,int shift = 0,int cut = 0x3f3f3f3f){
static int st[2][maxn];
int L = 1 << lg[n+m] + 1;
rep(i,0,L-1) st[0][i] = i <= n ? A[i] : 0 ,
st[1][i] = i <= m ? B[i] : 0;
NTT(st[0],L,1) , NTT(st[1],L,1);
rep(i,0,L-1) st[0][i] = 1ll * st[0][i] * st[1][i] % mod;
NTT(st[0],L,-1);
rep(i,shift,min(cut,n+m)) C[i - shift] = st[0][i];
}
#define lc u<<1
#define rc lc|1
int *B[maxn];
void Build2(int u,int l,int r){
B[u] = new int [r - l + 2];
if(l == r) return (void)(B[u][0] = 1 - p[l] , B[u][1] = p[l]);
int m = l + r >> 1;
Build2(lc,l,m) , Build2(rc,m+1,r);
Mul(B[lc],B[rc],B[u],m-l+1,r-m);
}
void Build(int u,int l,int r){
P[u] = new int [r-l+2];
P2[u] = new int [r-l+2];
F[u] = new int [4 * (r-l+2)];
G[u] = new int [4 * (r-l+2)];
memset(P[u],0,sizeof (int) * (r-l+2));
memset(P2[u],0,sizeof (int) * (r-l+2));
memset(F[u],0,sizeof (int) * 4 * (r-l+2));
memset(G[u],0,sizeof (int) * 4 * (r-l+2));
if(l == r) return (void)(
P[u][0] = 1 - p[l+1] , P[u][1] = p[l+1] ,
P2[u][0] = 1 - (l - L >= 1 ? p[l - L] : 0) , P2[u][1] = (l - L >= 1 ? p[l-L] : 0)
);
int m = l + r >> 1;
Build(lc,l,m) , Build(rc,m+1,r);
Mul(P[lc],P[rc],P[u],m-l+1,r-m);
Mul(P2[lc],P2[rc],P2[u],m-l+1,r-m);
if(l + L < r){
memset(P2[rc],0,4 * (r-m+1));
if(l + L <= m){
P2[rc][0] = 1;
}
else{
Build2(1,max(m+1-L,1),l);
rep(i,0,l-max(m+1-L,1)+1) P2[rc][i] = B[1][i];
}
}
if(r - L + 1 > l + 1){
//printf("#%d %d\n",l,r);
memset(P[lc],0,4 * (m-l+2));
if(r - L + 1 > m + 1){
P[lc][0] = 1;
}
else{
Build2(1,r-L+1,m+1);
rep(i,0,m+1-(r-L+1)+1) P[lc][i] = B[1][i];
}
}
}
void Solve(int u,int l,int r){
if(l == r){
//printf("@%d %d %d\n",l,r,F[u][1]);
f[l] = 1ll * f[l] * (1 + F[u][1]) % mod;
return;
}
int m = l + r >> 1;
Mul(F[u],P2[rc],F[lc],2*(r-l+1)-1,r-m,r-m,r-m+(m-l+1)*2);
Mul(G[u],P2[rc],G[lc],2*(r-l+1)-1,r-m,0,(m-l+1)*2);
Solve(lc,l,m);
static int t[maxn];
Mul(G[u],P[lc],G[rc],2*(r-l+1)-1,m+1-l,0,(r-m)*2);
Mul(F[u],P[lc],F[rc],2*(r-l+1)-1,m+1-l,(m+1-l)*2,(m+1-l)*2 + (r-m)*2);
Mul(&f[l],G[rc],t,m-l,(r-m)*2);
rep(i,0,(r-m)*2) F[rc][i+(l+r-2*m-1)] = (F[rc][i+(l+r-2*m-1)] + t[i]) % mod /*, printf("@%d %d %d\n",i,(l+r-2*m-1),t[i])*/;
Solve(rc,m+1,r);
}
int main(){
freopen("friends.in","r",stdin);
freopen("friends.out","w",stdout);
scanf("%d%d",&n,&L);
int sm = 1;
rep(i,1,n){
int a,b;scanf("%d%d",&a,&b);
p[i] = 1ll * a * Pow(b , mod - 2) % mod;
}
reverse(p+1,p+n+1);
rep(i,1,n){
sm = 1ll * sm * (1 - p[i]) % mod * (i - L >= 1 ? Pow(1 - p[i-L] , mod-2) : 1) % mod;
if(i >= L) f[i] = Pow(1 - sm , mod-2);
}
init(n << 2);
Build(1,1,n);
G[1][0] = 1;
Solve(1,1,n);
printf("%d\n",(f[n]+mod)%mod);
}