NOI2017 泳池

久莲是个爱玩的女孩子。

暑假终于到了,久莲决定请她的朋友们来游泳,她打算先在她家的私人海滩外圈一块长方形的海域作为游泳场。然而大海里有着各种各样的危险,有些地方水太深,有些地方有带毒的水母出没。她想让圈出来的这一块海域都是安全的。

经过初步分析,这块海域可视为一个底边长为 N 米,高为 1001 米的长方形网格。其中网格的底边对应着她家的私人海滩,每一个 1m×1m 的小正方形都代表着一个单位海域。她拜托了她爸爸明天去测量每一个小正方形是否安全。在得知了信息之后,她要做的就是圈出她想要的游泳场啦。

她心目中理想的游泳场满足如下三个条件:

  • 必须保证安全性。即游泳场中的每一个单位海域都是安全的。
  • 必须是矩形。即游泳场必须是整个网格中的一个 a×b 的子网格。
  • 必须和海滩相邻。即游泳场的下边界必须紧贴网格的下边界。

例如:当 N=5 时,若测量的结果如下(因为 1001 太大,这儿只画出网格最下面三行的信息,其他部分都是危险的)

那么她可以选取最下面一行的 1×4 的子海域,也可以选择第三列的 3×1 的子海域。注意她不能选取最上面一行的 1×5 的子海域,因为它没有与海滩相邻。

为了让朋友们玩的开心,她想让游泳场的面积尽可能的大。因此她会选取最下面那一行的 1×4 的子海域作为最终方案。

虽然她要明天才能知道每一个单位海域是否安全,但是她现在就想行动起来估计一下她的游泳场面积有多大。经过简单的估计,她假设每一个单位海域都有独立的 q 的概率是安全的,1−q 的概率是不安全的。她想要知道她能选择的最大的游泳场的面积恰好为 K 的概率是多少。

然而久莲对数学并不感兴趣,因此她想让你来帮她计算一下这个数值。

输入描述

输入一行四个正整数 N,K,x,y,其中 1≤x<y<998244353。q 的取值为 yx​。

输出描述

输出一行一个整数表示答案在模 998244353 意义下的取值。

即设答案化为最简分式后的形式为 ba​ ,其中 a 和 b 的互质。输出整数 x 使得 bx≡amod998244353 且 0≤x<998244353。可以证明这样的整数 x 是唯一的。

样例输入 

10 5 1 2

样例输出 

342025319

提示

数据范围与提示
测试点编号NK
1,2=1≤1000
3≤10≤8
4≤10≤9
5≤10≤10
6≤1000≤7
7≤1000≤8
8≤1000≤9
9,10,11≤1000≤100
12,13,14≤1000≤1000
15,16≤109≤10
17,18≤109≤100
19,20≤109≤1000

代码实现如下:

#include <bits/stdc++.h>
using namespace std;

#define Set(a, v) memset(a, v, sizeof(a))
#define For(i, a, b) for(int i = (a); i <= (int)(b); ++i)
#define Forr(i, a, b) for(int i = (a); i >= (int)(b); --i)

#define N (1000 + 5)
const int P = 998244353;

inline int Mod(int a){
    if(a < 0) return a + P;
    return a >= P? a - P: a;
}
inline int Mul(int a, int b){
    return (long long)a * b % P;
}

inline int Pow(int a, int anum){
    int ret = 1;
    while(anum){
        if(anum & 1) ret = Mul(ret, a);
        a = Mul(a, a); anum >>= 1;
    }
    return ret;
}

int n, x, y, p, q, powq[N], f[N][N], g[N], h[N], b[N], c[N], tmp[N << 1];

void ExpMul(int *A, int *B, int k){
    For(i, 0, k) For(j, 0, k) tmp[i + j] = Mod(tmp[i + j] + Mul(A[i], B[j]));

    Forr(i, k << 1, k){
        For(j, 0, k - 1) tmp[i + j - k] = Mod(tmp[i + j - k] + Mul(tmp[i], g[k - j]));
        tmp[i] = 0;
    }

    For(i, 0, k - 1) A[i] = tmp[i], tmp[i] = 0;
}

void ExpPow(int *A, int anum, int *ret, int k){
    while(anum){
        if(anum & 1) ExpMul(ret, A, k);
        ExpMul(A, A, k); anum >>= 1;
    }
}

int Solve(int k){
    if(!k) return Pow(p, n);

    Set(f, 0); f[k + 1][0] = 1;

    Forr(i, k, 1){
        int m = min(n, k / i);

        f[i][0] = 1;
        For(j, 0, m) g[j] = Mul(Mul(f[i + 1][j], powq[j]), p);

        For(j, 1, m){
            For(t, 0, j - 1) f[i][j] = Mod(f[i][j] + Mul(g[t], f[i][j - t - 1]));
            f[i][j] = Mod(f[i][j] + Mul(f[i + 1][j], powq[j]));
        }
    }

    ++k;
    For(i, 1, k) g[i] = Mul(Mul(f[1][i - 1], powq[i - 1]), p);

    h[0] = 1;
    For(i, 1, k - 1){
        h[i] = Mul(powq[i], f[1][i]);
        For(j, 1, i) h[i] = Mod(h[i] + Mul(h[i - j], g[j]));
    }

    if(n < k) return h[n];

    int ret = 0;

    Set(b, 0); Set(c, 0); b[0] = c[1] = 1;
    ExpPow(c, n, b, k);

    For(i, 0, k - 1) ret = Mod(ret + Mul(b[i], h[i]));

    return ret;
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("pool.in", "r", stdin);
    freopen("pool.out", "w", stdout);
#endif

    int k;

    scanf("%d%d%d%d", &n, &k, &x, &y);  
    q = Mul(x, Pow(y, P - 2)); p = Mod(1 - q);

    powq[0] = 1;
    For(i, 1, 1000) powq[i] = Mul(powq[i - 1], q);

    printf("%d\n", Mod(Solve(k) - Solve(k - 1)));

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值