Loj #2304. 「NOI2017」泳池

Loj #2304. 「NOI2017」泳池

題目描述

久蓮是個愛玩的女孩子。c++

暑假終於到了,久蓮決定請她的朋友們來游泳,她打算先在她家的私人海灘外圈一塊長方形的海域做爲游泳場。然而大海里有着各類各樣的危險,有些地方水太深,有些地方有帶毒的水母出沒。她想讓圈出來的這一塊海域都是安全的。安全

通過初步分析,這塊海域可視爲一個底邊長爲 \(N\) 米,高爲 \(1001\) 米的長方形網格。其中網格的底邊對應着她家的私人海灘,每個 \(1\:\textrm{m}\times1\:\textrm{m}\) 的小正方形都表明着一個單位海域。她拜託了她爸爸明天去測量每個小正方形是否安全。在得知了信息以後,她要作的就是圈出她想要的游泳場啦。ui

她心目中理想的游泳場知足以下三個條件:es5

* 必須保證安全性。即游泳場中的每個單位海域都是安全的。spa

* 必須是矩形。即游泳場必須是整個網格中的一個 \(a\times b\) 的子網格。3d

* 必須和海灘相鄰。即游泳場的下邊界必須緊貼網格的下邊界。code

例如:當 \(N = 5\) 時,若測量的結果以下(由於 \(1001\) 太大,這兒只畫出網格最下面三行的信息,其餘部分都是危險的)。blog

Screen Shot 2017-07-22 at 4.54.14 PM.png

那麼她能夠選取最下面一行的 \(1\times4\) 的子海域,也能夠選擇第三列的 \(3\times1\) 的子海域。注意她不能選取最上面一行的 \(1\times5\) 的子海域,由於它沒有與海灘相鄰。get

爲了讓朋友們玩的開心,她想讓游泳場的面積儘量的大。所以她會選取最下面那一行的 \(1\times4\) 的子海域做爲最終方案。數學

雖然她要明天才能知道每個單位海域是否安全,可是她如今就想行動起來估計一下她的游泳場面積有多大。通過簡單的估計,她假設每個單位海域都有獨立的 \(q\) 的機率是安全的,\(1 − q\) 的機率是不安全的。她想要知道她能選擇的最大的游泳場的面積剛好\(K\) 的機率是多少。

然而久蓮對數學並不感興趣,所以她想讓你來幫她計算一下這個數值。

輸入格式

輸入一行四個正整數 \(N,K,x,y\),其中 \(1 \leq x < y < 998244353\)\(q\) 的取值爲 \(\frac{x}{y}\)

輸出格式

輸出一行一個整數表示答案在模 \(998244353\) 意義下的取值。

即設答案化爲最簡分式後的形式爲 \(\frac{a}{b}\) ,其中 \(a\)\(b\) 的互質。輸出整數 \(x\) 使得 \(bx \equiv a \mod 998244353\)\(0 \leq x < 998244353\)。能夠證實這樣的整數 \(x\) 是惟一的。

數據範圍與提示

\(N\leq 10^9,K\leq 1000\)


神仙題。

咱們能夠用求最大面積\(\leq k\)的機率減去最大面積\(\leq k+1\)的機率獲得答案。

先考慮\(n\)比較小的狀況。設\(f_{i,j}\)表示有\(i\)列,\(1\)\(j\)行都安全時最大矩陣\(\leq k\)的機率。對於\(i*j>k\)的狀態,\(f_{i,j}=0\)

轉移時,能夠考慮第\(j+1\)行是否所有沒有危險,不然枚舉從左往右第一個有危險的地方。因而獲得轉移方程:
\[ f_{i,j}=f_{i,j+1}*q^i+\sum_{k=1}^if_{k-1,j+1}*q^{k-1}*(1-q)*f_{i-k,j} \]
因爲有效狀態只有\(k\)個,轉移複雜度也是\(O(k)\),因此這個\(DP\)的複雜度\(O(k^2)\)。答案就是\(f_{n,0}\)

考慮\(n\)很大的狀況,由於不可能出現連續的\(k+1\)列至少第一行是安全的狀況,因此一個合法的矩陣必定是若干段矩陣,中間用第一行的危險的格子鏈接。

\(F_i\)表示\(i\)列的機率,則:
\[ F_i=\sum_{j=i-k}^iF_{j-1}*(1-q)*f_{i-j,1}*q^{i-j} \]
能夠發現這就是一個\(k\)階其次線性遞推。因此咱們能夠將轉移寫成
\[ F_i=\sum_{j=0}^{k}F_{i-k-1+j}*a_j \]
其中\(a_i=(1-q)*f_{k-i,1}*q^{k-i}\)

咱們發現這就是個\(k\)階齊次線性遞推。

\(A(x)=x^{k+1}-\sum_{i=0}^ka_ix^i,B(x)=x^n\),而後咱們要求出\(C(x)=B(x)\%A(x)\),答案就是\(\sum_{i=0}^k c_i*F_i\),其中\(F_i=f_{i,0}\)

可是\(C(x)\)最高次能夠達到\(10^9\),不可能直接取模。

由:
\[ f(x)\equiv g(x)\pmod{A(x)}\\ f(x)^2\equiv g(x)^2\pmod{A(x)}\\ \]
因而就能夠用相似於快速冪的方法來求\(x^n\%A(x)\)

代碼:

#include<bits/stdc++.h>
#define ll long long
#define K 1005

using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}

const ll mod=998244353;
ll ksm(ll t,ll x) {
    ll ans=1;
    for(;x;x>>=1,t=t*t%mod)
        if(x&1) ans=ans*t%mod;
    return ans;
}

const int maxx=1001;
int n,k;
ll q,p;
ll f[K][K];
ll F[K];
ll G[K],a[K];
ll r[K],b[K];

void mul(ll *x,ll *y,ll *ans,int len) {
    static ll tem[K<<1];
    for(int i=0;i<2*len;i++) tem[i]=0;
    for(int i=0;i<len;i++)
        for(int j=0;j<len;j++)
            (tem[i+j]+=x[i]*y[j])%=mod;
    for(int i=2*len-1;i>=len;i--) {
        if(tem[i]) {
            for(int j=0;j<len;j++) (tem[i-len+j]+=a[j]*tem[i])%=mod;
        }
    }
    for(int i=0;i<len;i++) ans[i]=tem[i];
}

ll solve(int lim) {
    memset(f,0,sizeof(f));
    memset(F,0,sizeof(F));
    memset(r,0,sizeof(r));
    memset(b,0,sizeof(b));
    for(int i=0;i<=maxx;i++) f[0][i]=1;
    for(int i=1;i<=1000;i++) {
        for(int j=1000;j>=0;j--) {
            if(i*j>lim) continue ;
            f[i][j]=f[i][j+1]*ksm(q,i)%mod;
            for(int k=1;k<=i;k++) {
                (f[i][j]+=f[k-1][j+1]*ksm(q,k-1)%mod*p%mod*f[i-k][j])%=mod;
            }
        }
    }
    for(int i=0;i<=lim;i++) a[lim-i]=f[i][1]*ksm(q,i)%mod*p%mod;
    r[0]=b[1]=1;
    int now=n;
    for(;now;now>>=1,mul(b,b,b,lim+1)) {
        if(now&1) mul(r,b,r,lim+1);
    }
    ll ans=0;
    for(int i=0;i<=lim;i++) (ans+=r[i]*f[i][0])%=mod;
    return ans;
}

int main() {
    n=Get(),k=Get();
    int x=Get(),y=Get();
    q=x*ksm(y,mod-2)%mod;
    p=(1-q+mod)%mod;
    cout<<(solve(k)-solve(k-1)+mod)%mod;
    return 0;
}
相關文章
相關標籤/搜索