总结
概率题一般正着推
期望题一般倒着推
图上的问题如果是 \(DAG\) 可以直接转移
否则可能要用到高斯消元
\(20\) 的数据范围大概率是装压
有些看似无限循环的式子其实可以倒着递推
1、骰子基础版
题目描述
众所周知,骰子是一个六面分别刻有一到六点的立方体,每次投掷骰子,从理论上讲得到一点到六点的概率都是 \(1/6\)。今有骰子一颗,连续投掷 \(N\)次 ,问点数总和大于等于 \(X\) 的概率是多少?
输入
仅有一行包含二个用空格隔开的整数,分别表示\(n\)和\(x\),其中\(1<=N<=24,0<=x<150\)。
输出
仅有一行包含一个有理数,要求以最简的形式精确地表达出连续投掷\(N\)次骰子,总点数大于等于\(X\)的概率。
样例输入
3 9
样例输出
20/27
分析
设 \(f[i][j]\) 为第 \(i\) 次投掷骰子且总得分为 \(j\) 的方案数
则\(f[i][j]+=f[i-1][k],1 \leq j-k \leq 6\)
其实 \(24^6\) 的暴力也可以过
代码
#include
const int maxn=30,maxm=200;
typedef long long ll;
ll f[maxn][maxm],tot,ans;
int n,x;
ll gcd(ll aa,ll bb){
if(bb==0) return aa;
return gcd(bb,aa%bb);
}
int main(){
scanf("%d%d",&n,&x);
f[0][0]=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=6;j++){
for(int k=i;k<=i*6;k++){
if(k
2、三角形的概率
题目描述
这是一道数学题。
随机产生 \(3\) 个一定范围内的正整数,作为一个三角形的三条边,求他们能构成一个三角形的概率是多少?
你能证明吗?
你能用代码验证一下吗?
输入格式
无
输出格式
一个浮点数,表示答案,保留三位小数。
数据范围与提示
用作图法来证明即可,自己算算
分析
其实是一道数学题
如果三条边 \(a,b,c\) 能构成三角形
必定有 \(a+b
移项会得到 \(\frac{a}{c}+\frac{b}{c}<1\)
实际上求的就是两个小于 \(1\) 的正数相加大于 \(1\) 的概率
用几何概型解决
画一个边长为为 \(1\) 的正方形,则在对角线上面的部分即为符合条件的,所以概率为\(0.5\)
代码
#include
int main(){
printf("0.500\n");
return 0;
}
3、聪聪和可可
题目描述
题目传送门
分析
我们发现猫的走位比较神奇,因此可以提前预处理出猫在位置 \(i\) 且老鼠在位置 \(j\) 时,猫下一步走到的位置 \(nxt[i][j]\)
预处理可以用最短路来实现
如果 \(i\) 到 \(j\) 有一条边,并且 \(k\) 到 \(i\) 的最短路等于 \(k\) 到 \(j\) 的最短路加 \(1\),则 \(nxt[i][k]=j\)
剩下的过程可以用记忆化搜索实现
代码
#include
#include
#include
#include
#include
inline int read(){
int x=0,fh=1;
char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=1e3+5;
int head[maxn],tot=1;
struct asd{
int to,next;
}b[maxn<<1];
void ad(int aa,int bb){
b[tot].to=bb;
b[tot].next=head[aa];
head[aa]=tot++;
}
int dis[maxn][maxn],nxt[maxn][maxn],n,m,s,t;
bool vis[maxn];
std::queue q;
void spfa(int qd){
memset(vis,0,sizeof(vis));
dis[qd][qd]=0;
q.push(qd);
vis[qd]=1;
while(!q.empty()){
int now=q.front();
q.pop();
vis[now]=0;
for(int i=head[now];i!=-1;i=b[i].next){
int u=b[i].to;
if(dis[qd][u]>dis[qd][now]+1){
dis[qd][u]=dis[qd][now]+1;
if(!vis[u]){
q.push(u);
vis[u]=1;
}
}
}
}
}
int du[maxn];
bool viss[maxn][maxn];
double f[maxn][maxn];
double dfs(int mao,int shu){
if(viss[mao][shu]) return f[mao][shu];
if(mao==shu) return 0;
int aa=nxt[mao][shu];
int bb=nxt[aa][shu];
if(aa==shu || bb==shu) return 1;
f[mao][shu]=1;
for(int i=head[shu];i!=-1;i=b[i].next){
int u=b[i].to;
f[mao][shu]+=dfs(bb,u)/(du[shu]+1);
}
f[mao][shu]+=dfs(bb,shu)/(du[shu]+1);
viss[mao][shu]=1;
return f[mao][shu];
}
int main(){
memset(head,-1,sizeof(head));
memset(dis,0x3f,sizeof(dis));
memset(nxt,0x3f,sizeof(nxt));
n=read(),m=read(),s=read(),t=read();
for(int i=1;i<=m;i++){
int aa,bb;
aa=read(),bb=read();
ad(aa,bb);
ad(bb,aa);
du[aa]++;
du[bb]++;
}
for(int i=1;i<=n;i++) spfa(i);
for(int i=1;i<=n;i++){
for(int j=head[i];j!=-1;j=b[j].next){
int u=b[j].to;
for(int k=1;k<=n;k++){
if(dis[i][k]-1==dis[u][k]){
nxt[i][k]=std::min(nxt[i][k],u);
}
}
}
}
printf("%.3f\n",dfs(s,t));
return 0;
}
4、OSU!
题目描述
题目传送门
分析
\((x+1)^{3}=x^3+3x^2+3x+1\)
每次多增加的部分是 \(3x^2+3x+1\)
我们再开两个数组分别维护 \(x^2\) 的期望和 \(x\) 的期望
\(f1[i]=(f1[i-1]+1)\times p[i]\)
\(f2[i]=(f2[i-1]+2 \times f1[i-1] +1) \times p[i]\)
\(f3[i]=(f3[i-1]+3 \times f2[i-1] +3 \times f1[i-1] +1) \times p[i] +f3[i-1] \times (1-p[i])\)
注意 \(f3[i]\) 还要算上不增加的贡献
代码
#include
const int maxn=1e5+5;
double f1[maxn],f2[maxn],f3[maxn],p[maxn],ans;
int n;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lf",&p[i]);
}
for(int i=1;i<=n;i++){
f1[i]=(f1[i-1]+1.0)*p[i];
f2[i]=(f2[i-1]+2.0*f1[i-1]+1)*p[i];
f3[i]=(f3[i-1]+3.0*f1[i-1]+3.0*f2[i-1]+1.0)*p[i]+(1-p[i])*f3[i-1];
}
printf("%.1f\n",f3[n]);
return 0;
}
5、守卫者的挑战
题目描述
打开了黑魔法师 \(Vani\) 的大门,队员们在迷宫般的路上漫无目的地搜寻着关押\(applepi\)的监狱的所在地。突然,眼前一道亮光闪过。“我,\(Nizem\),是黑魔法圣殿的守卫者。如果你能通过我的挑战,那么你可以带走黑魔法圣殿的地图……”瞬间,队员们被传送到了一个擂台上,最初身边有一个容量为\(K\)的包包。
擂台赛一共有\(N\)项挑战,各项挑战依次进行。第\(i\)项挑战有一个属性\(a_i\),如果\(a_i>=0\),表示这次挑战成功后可以再获得一个容量为\(a_i\)的包包;如果\(a_i=-1\),则表示这次挑战成功后可以得到一个大小为\(1\) 的地图残片。地图残片必须装在包包里才能带出擂台,包包没有必要全部装满,但是队员们必须把 【获得的所有的】地图残片都带走(没有得到的不用考虑,只需要完成所有\(N\)项挑战后背包容量足够容纳地图残片即可),才能拼出完整的地图。并且他们至少要挑战成功\(L\)次才能离开擂台。
队员们一筹莫展之时,善良的守卫者\(Nizem\)帮忙预估出了每项挑战成功的概率,其中第\(i\)项挑战成功的概率为\(p_i\%\)。现在,请你帮忙预测一下,队员们能够带上他们获得的地图残片离开擂台的概率。
输入格式
第一行三个整数\(N,L,K\)。
第二行\(N\)个实数,第\(i\)个实数\(p_i\)表示第\(i\)项挑战成功的百分比。
第三行\(N\)个整数,第\(i\)个整数\(a_i\)表示第\(i\)项挑战的属性值.
输出格式
一个整数,表示所求概率,四舍五入保留\(6\) 位小数。
样例
样例输入1
3 1 0
10 20 30
-1 -1 2
样例输出1
0.300000
样例输入2
5 1 2
36 44 13 83 63
-1 2 -1 2 1
样例输出2
0.980387
数据范围与提示
若第三项挑战成功,如果前两场中某场胜利,队员们就有空间来容纳得到的地图残片,如果挑战失败,根本就没有获得地图残片,不用考虑是否能装下;
若第三项挑战失败,如果前两场有胜利,没有包来装地图残片,如果前两场都失败,不满足至少挑战成功次()的要求。因此所求概率就是第三场挑战获胜的概率。
对于 \(100\%\) 的数据,保证\(0<=K<=2000\),\(0<=N<=200\),\(-1<=ai<=1000\),\(0<=L<=N\),\(0<=p_i<=100\)。
分析
设 \(f[i][j][k]\) 为前 \(i\) 回合赢了 \(j\) 回合剩余背包体积为 \(k\) 的概率
其中转移过程中体积可以为负数,因为可以暂时不把碎片放到背包里
转移方程为
\(f[i][j][k+a[i]]=f[i-1][j][k+a[i]] \times (1.0-p[i])\)
\(if(j) f[i][j][k+a[i]]=f[i-1][j-1][k] \times p[i]\)
代码
#include
#include
#include
const int maxn=205;
const int bas=200;
double f[maxn][maxn][maxn<<1],p[maxn];
int n,l,k,a[maxn];
int main(){
scanf("%d%d%d",&n,&l,&k);
for(int i=1;i<=n;i++){
int aa;
scanf("%d",&aa);
p[i]=aa/100.0;
}
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
f[0][0][k+bas]=1;
for(int i=1;i<=n;i++){
for(int j=0;j<=i;j++){
for(int k=-200;k<=200;k++){
f[i][j][std::min(k+bas+a[i],400)]=f[i-1][j][std::min(k+bas+a[i],400)]*(1.0-p[i]);
if(j) f[i][j][std::min(k+bas+a[i],400)]+=f[i-1][j-1][std::min(k+bas,400)]*p[i];
}
}
}
double ans=0;
for(int i=l;i<=n;i++){
for(int j=bas;j<=200+bas;j++){
ans=ans+f[n][i][j];
}
}
printf("%.6f\n",ans);
return 0;
}
6、 Easy
题目描述
某一天\(WJMZBMR\)在打\(osu\)但是他太弱逼了,有些地方完全靠运气:(
我们来简化一下这个游戏的规则
有 \(n\) 次点击要做,成功了就是\(o\),失败了就是\(x\),分数是按\(comb\)计算的,连续\(a个comb\)就有a \times a分,\(comb\)就是极大的连续\(o\)。比如ooxxxxooooxxx,分数就是\(2 \times 2+4 \times4=4+16=20\)。
\(Sevenkplus\)闲的慌就看他打了一盘,有些地方跟运气无关要么是\(o\)要么是\(x\),有些地方\(o\)或者\(x\)各有\(50\%\)的可能性,用\(?\)号来表示。比如\(oo?xx\)就是一个可能的输入。
那么\(WJMZBMR\)这场\(osu\)的期望得分是多少呢?比如\(oo?xx\)的话,\(?\)是\(o\)的话就是\(oooxx => 9\),是\(x\)的话就是\(ooxxx => 4\) 期望自然就是\((4+9)/2 =6.5\)了
输入格式
第一行一个整数\(n\),表示点击的个数
接下来一个字符串,每个字符都是\(ox?\)中的一个
输出格式
一行一个浮点数表示答案
四舍五入到小数点后\(4\)位
如果害怕精度跪建议用\(long double\)或者\(extended \)
样例
样例输入
4
????
样例输出
4.1250
数据范围与提示
\(n<=300000\)
\(osu很好玩的哦\)
\(WJMZBMR\)技术还行(雾),\(x\)基本上很少呢
分析
第 \(4\) 题的弱化版
代码
#include
using namespace std;
typedef double dd;
const int maxn=1e6+5;
dd g[maxn],f[maxn];
char s[maxn];
int main(){
int n;
scanf("%d",&n);
scanf("%s",s+1);
for(int i=1;i<=n;i++){
if(s[i]=='o'){
f[i]=f[i-1]+2*g[i-1]+1;
g[i]=g[i-1]+1;
}
else if(s[i]=='x'){
f[i]=f[i-1];
g[i]=0;
} else{
g[i]=(g[i-1]+1)/2.0;
f[i]=0.5*f[i-1]+0.5*(f[i-1]+2*g[i-1]+1);
}
}
printf("%.4lf\n",f[n]);
return 0;
}
7、单选错位
题目描述
题目传送门
分析
我们考虑 \(gx\) 期望做对的第 \(i+1\) 道题的概率
如果 \(a[i] \geq a[i+1]\) ,则有 \(\frac{a[i+1]}{a[i]}\)的概率落到正确答案的区间内,同时答对的可能性为 \(\frac{1}{a[i+1]}\)
如果 \(a[i] < a[i+1]\) ,则有 \(\frac{a[i]}{a[i+1]}\)的概率落到正确答案的区间内,同时答对的可能性为 \(\frac{1}{a[i]}\)
因此最终的答案为 \(\sum_{i=1}^n \frac{1}{max(a[i],a[i+1])}\)
代码
#include
#include
#include
const int maxn=10000005;
int n,A,B,C;
long long a[maxn];
double ans;
int main(){
scanf("%d%d%d%d%lld",&n,&A,&B,&C,&a[1]);
for(int i=2;i<=n;i++){
a[i] = ((long long)a[i-1] * A + B) % 100000001;
}
for(int i=1;i<=n;i++){
a[i]=a[i]%C+1;
}
a[0]=a[n];
for(int i=1;i<=n;i++){
ans+=1.0/(std::max(a[i],a[i-1]));
}
printf("%.3f\n",ans);
return 0;
}
8、洛谷P2059 [JLOI2013]卡牌游戏
题目描述
题目传送门
分析
这道题正着不好处理,倒着设比较方便
设 \(f[i][j]\) 为 \(i\) 人形成的环中,第 \(j\) 个人获胜的概率
已知 \(f[1][1]=1\)
那么我们就可以模拟抽走每一张牌,计算出剩下的人在更小的环里对应的位置
然后用之前已经求得的值更新当前值
代码
#include
using namespace std;
const int maxn=55;
double f[maxn][maxn];
int a[maxn];
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d",&a[i]);
}
f[1][1]=1.0;
for(int i=2;i<=n;i++){
for(int j=1;j<=i;j++){
for(int k=1;k<=m;k++){
int p=a[k]%i;
if(p==0) p=i;
if(p>j) f[i][j]+=f[i-1][i-p+j]/m;
else f[i][j]+=f[i-1][j-p]/m;
}
}
}
for(int i=1;i<=n;i++){
printf("%.2lf%% ",f[n][i]*100.0);
}
return 0;
}
9、洛谷 P3232 [HNOI2013]游走
题目描述
游走
分析
因为要使获得总分的期望值最小
所以我们肯定要给经过次数少的边赋大权值
但是边的期望经过次数不好直接求
但是我们可以求出点的期望经过次数
边的期望经过次数就是它所连点的期望经过次数除以点的入度再加和
我们设点 \(u\) 的期望经过次数为 \(f[u]\)
那么 \(f[u]= \sum_{v-u}f[v]/du[v]\)
其中 \(du[v]\) 是节点 \(v\) 的入度
初始化 \(f[n]=1\)
要注意的是当 \(u=1\) 时,还要把 \(f[u]\) 加上 \(1\)
因为一开始是从 \(1\) 号节点出发的
然后就可以高斯消元求解了
代码
#include
#include
#include
#include
#include
inline int read(){
int x=0,fh=1;
char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=505,maxm=125005;
const double eqs=1e-10;
int n,m,head[maxn],tot=1;
struct asd{
int from,to,next;
}b[maxm<<1];
void ad(int aa,int bb){
b[tot].from=aa;
b[tot].to=bb;
b[tot].next=head[aa];
head[aa]=tot++;
}
int du[maxn];
double mp[maxn][maxn],ans[maxn];
void gsxy(){
int now=1;
for(int i=1;i<=n;i++){
double mmax=0;
int jl;
for(int j=now;j<=n;j++){
if(std::fabs(mmax)=1;i--){
ans[i]=mp[i][n+1];
for(int j=i+1;j<=n;j++){
ans[i]-=ans[j]*mp[i][j];
}
}
}
std::priority_queue q;
int main(){
memset(head,-1,sizeof(head));
n=read(),m=read();
for(int i=1;i<=m;i++){
int aa,bb;
aa=read(),bb=read();
ad(aa,bb);
ad(bb,aa);
du[aa]++;
du[bb]++;
}
for(int i=1;i<=n;i++){
for(int j=head[i];j!=-1;j=b[j].next){
int u=b[j].to;
if(u==n) continue;
mp[i][u]=-1.0/du[u];
}
}
for(int i=1;i<=n;i++){
mp[i][i]=1;
}
for(int i=1;i
10、洛谷P4284 [SHOI2014]概率充电器
题目描述
题目传送门
分析
我们设 \(f[u]\) 为节点 \(u\) 不被点亮的概率
那么需要满足 \(u\) 既不会自己点亮自己,也不会被与它相邻的点点亮
我们可以任选一个点作为根节点,求出节点 \(u\) 只被儿子节点影响的概率
然后再通过换根 \(DP\) 求出以其它节点为根的情况
代码
#include
#include
const int maxn=1e6+5;
int head[maxn],tot=1,n;
struct asd{
int to,next;
double val;
}b[maxn];
void ad(int aa,int bb,int cc){
b[tot].to=bb;
b[tot].next=head[aa];
b[tot].val=(double)cc*0.01;
head[aa]=tot++;
}
double f[maxn],p[maxn],g[maxn];
void dfs(int now,int fa){
f[now]=p[now];
for(int i=head[now];i!=-1;i=b[i].next){
int u=b[i].to;
if(u==fa) continue;
dfs(u,now);
f[now]*=(1.0-b[i].val+b[i].val*f[u]);
}
}
void dfs2(int now,int fa,double lat){
if(now==1){
g[now]=f[now];
} else {
double P=g[fa]/(1.0-lat+lat*f[now]);
g[now]=f[now]*(1.0-lat+lat*P);
}
for(int i=head[now];i!=-1;i=b[i].next){
int u=b[i].to;
if(u==fa) continue;
dfs2(u,now,b[i].val);
}
}
int main(){
memset(head,-1,sizeof(head));
scanf("%d",&n);
for(int i=1;i
11、一个人的游戏
分析
这道题通过带换系数的方法解决,思路不错
详解
12、Gambling Guide
分析
传送门
13、换教室
题目描述
题目传送门
分析
设 \(f[i][j][0]\) 为截止到第 \(i\) 节课,一共换了 \(j\) 次,其中第 \(i\) 节课没有申请换教室的耗费体力值的最小期望
设 \(f[i][j][1]\) 为截止到第 \(i\) 节课,一共换了 \(j\) 次,其中第 \(i\) 节课申请换教室的耗费体力值的最小期望
状态转移就很简单了
代码
#include
#include
#include
#include
const int maxn=2e3+5;
int f[maxn][maxn];
int n,m,e,v,c[maxn],d[maxn];
typedef double db;
db k[maxn];
db dp[maxn][maxn][2];
int main(){
memset(f,0x3f,sizeof(f));
scanf("%d%d%d%d",&n,&m,&v,&e);
for(int i=1;i<=n;i++){
scanf("%d",&c[i]);
}
for(int i=1;i<=n;i++){
scanf("%d",&d[i]);
}
for(int i=1;i<=n;i++){
scanf("%lf",&k[i]);
}
for(int i=1;i<=v;i++){
f[i][i]=0;
}
for(int i=0;i<=n;i++){
for(int j=0;j<=m;j++){
dp[i][j][0]=dp[i][j][1]=1e18;
}
}
for(int i=1;i<=e;i++){
int aa,bb,cc;
scanf("%d%d%d",&aa,&bb,&cc);
if(f[aa][bb]>cc) f[aa][bb]=f[bb][aa]=cc;
}
for(int kk=1;kk<=v;kk++){
for(int i=1;i<=v;i++){
for(int j=1;j<=v;j++){
f[i][j]=std::min(f[i][j],f[i][kk]+f[kk][j]);
}
}
}
dp[1][1][1]=dp[1][0][0]=0;
for(int i=2;i<=n;i++){
for(int j=0;j<=std::min(i,m);j++){
if(j)dp[i][j][1]=std::min(dp[i][j][1],dp[i-1][j-1][0]+k[i]*f[c[i-1]][d[i]]+(1-k[i])*f[c[i-1]][c[i]]);
if(j)dp[i][j][1]=std::min(dp[i][j][1],dp[i-1][j-1][1]+k[i-1]*k[i]*f[d[i-1]][d[i]]+(1-k[i])*(1-k[i-1])*f[c[i-1]][c[i]]+k[i-1]*(1-k[i])*f[d[i-1]][c[i]]+k[i]*(1-k[i-1])*f[d[i]][c[i-1]]);
dp[i][j][0]=std::min(dp[i][j][0],dp[i-1][j][0]+f[c[i]][c[i-1]]);
dp[i][j][0]=std::min(dp[i][j][0],dp[i-1][j][1]+k[i-1]*f[d[i-1]][c[i]]+(1-k[i-1])*f[c[i]][c[i-1]]);
}
}
double ans=1e18;
for(int i=0;i<=m;i++){
ans=std::min(ans,dp[n][i][0]);
ans=std::min(ans,dp[n][i][1]);
}
printf("%.2f\n",ans);
return 0;
}
14、跳一跳
题目描述
题目传送门
分析
设 \(f[i]\) 为当前在 \(i\) 点,到达 \(n\) 点的期望时间
转移很简单,但是注意要把变量滚一下
因为本题卡空间
代码
#include
const int maxn=1e7+5;
const int mod=1e9+7;
int ny[maxn],n,ans,sum;
int main(){
scanf("%d",&n);
ny[1]=1;
for(int i=2;i<=n;i++){
ny[i]=1LL*(mod-mod/i)*ny[mod%i]%mod;
}
ans=0;
for(int i=n-1;i>=1;i--){
ans=1LL*(sum+n-i+1LL)*ny[n-i]%mod;
sum=(sum+ans)%mod;
}
printf("%d\n",ans);
return 0;
}
15、分手是祝愿
题目描述
题目描述
分析
其实这道题不是很难
但是题面往往具有迷惑性
我们可以发现每个按键都不可能被其他按键的组合键替代
于是我们实际上可以从大到小扫一遍,碰到亮的就按一遍,这样的话我们就可以找到所有必须要按的键
我们设 \(f[i]\) 为从第 \(i\) 个需要的键按到第 \(i-1\) 个需要的键期望按的次数
那么就有
\(f[i]=\frac{i}{n}+\frac{n-i}{n} \times (f[i]+f[i+1]+1)\)
移项化简就可以了
代码
#include
#include
const int maxn=1e5+5;
const int mod=100003;
inline int read(){
int x=0,fh=1;
char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
std::vector g[maxn];
int n,a[maxn],f[maxn],ny[maxn],k;
void pre(int now){
for(int i=1;i<=now;i++){
int mmax=now/i;
for(int j=1;j<=mmax;j++){
g[i*j].push_back(i);
}
}
}
int main(){
n=read(),k=read();
for(int i=1;i<=n;i++){
a[i]=read();
}
pre(n);
ny[1]=1;
for(int i=2;i=1;i--){
if(a[i]==1){
for(int j=0;j=1;i--){
f[i]=1LL*(n+1LL*(n-i)*f[i+1]%mod)%mod*ny[i]%mod;
}
int ans=0;
if(k>=cs){
ans=cs;
} else {
for(int i=k+1;i<=cs;i++){
ans=(ans+f[i])%mod;
}
ans=(ans+k)%mod;
}
for(int i=1;i<=n;i++){
ans=1LL*ans*i%mod;
}
printf("%d\n",ans);
return 0;
}