概率期望简单杂题

前言

云剪切板 link

cnblogs 我相信你!所以我把所有博客题解链都展开了!

有删改


ああウー…… 转载请注明出处 .


概率期望小记

为了省空间,代码压缩了(用的 Mivik 的代码压行机),想看可以自己格式化一下 .

缺省源:头文件 \(14,15\) 题缺省高斯消元)

映射表

题号 A B C D E F G H I J K L M N O P Q R
迫真题号 \(1\) \(2\) \(3\) \(4\) \(5\) \(6\) \(7\) \(8\) \(9\) \(10\) \(11\) \(12\) \(13\) \(14\) \(15\) \(16\) \(17\) \(18\)
题目名 绿豆蛙的归宿 聪聪和可可 OSU! Red is good 守卫者的挑战 Easy 单选错位 列队春游 矩形粉刷 卡牌游戏 换教室 奖励关 概率充电器 游走 XOR和路径 分手是祝愿 硬币游戏
是否用到 dp(Y/N) Y Y Y Y Y Y Y N N Y Y Y Y Y Y Y Y N

Upd. S, T 题(chemistry,抽卡)没了,只剩 \(18\) 道题了


1. 绿豆蛙的归宿

题面

链接


一张 \(n\) 个点的带权 DAG 上随机游走,问 \(1\) 走到 \(n\) 走过的路径边权和期望 .

简要题解

简单题,令 \(dp_u\) 为从 \(1\)\(n\) 的版边和期望,则由期望定义知

\[dp_u=\dfrac1{\deg u}\sum_{v\to u}(dp_v+\operatorname{val}(u, v)) \]

其中 \(\deg u\)\(u\) 的入度 .

转移顺序是倒着的拓扑序 .

代码

using namespace std;const int N=1e5+500;typedef long long ll;typedef double db;int n,m,deg[N],ii[N];bool vis[N];db dp[N];vector<pair<int,int>>g[N];inline void addedge(int u,int v,int w){g[u].emplace_back(make_pair(v,w));++deg[v];++ii[v];}void koishi(int s){queue<int>q;q.push(s);dp[s]=0;while(!q.empty()){int u=q.front();q.pop();for(auto e:g[u]){int v=e.first,w=e.second;dp[v]+=(dp[u]+w)/deg[v];if(!--ii[v])q.push(v);}}}int main(){scanf("%d%d",&n,&m);for(int i=0,u,v,w;i<m;i++)scanf("%d%d%d",&u,&v,&w),addedge(v,u,w);koishi(n);printf("%.2f",dp[1]);return 0;}

2. 聪聪和可可

题面

链接


一张 \(n\) 个点 \(m\) 条边的无向图,俩小可爱 \(A,B\) 在图上走,初始 \(A\) 在点 \(m\)\(B\) 在点 \(c\),每个时刻 \(A,B\) 的走法如下:

  • \(A\) 在图上随机游走(\(A\) 可能原地不动)
  • \(B\) 的策略是每次选一个最接近 \(A\) 的出点,如果没到 \(A\) 就再走一步 .

每个时刻 \(A\) 先走,\(B\) 后走,问期望走几个时刻 \(A,B\) 相遇 .

简要题解

第二题就放毒瘤,呜呜

首先这个 \(B\) 的迷の走位可以预处理:令 \(to(i,j)\) 表示 \(B\)\(i\)\(A\)\(j\)\(B\) 应该走到哪 .

\(dp_{i,j}\) 表示 \(B\)\(i\)\(A\)\(j\) 的方案数,则

  • \(i=j\),则 \(dp_{i,j}=0\) .
  • \(i\)\(1,2\) 步就到 \(A\) 了,则 \(dp_{i,j}=1\) .
  • 若不然,则:

\[dp_{i,j}=1+\dfrac1{\deg j}\sum_{j\to k\text{ or }j=k}dp_{tr(i,k),k} \]

其中 \(tr(i,k)=to(to(i,k),k)\),即 \(i\) 走两步 .

照着方程跑 dp,做完了!

代码

using namespace std;const int N=1145;typedef long long ll;typedef double db;int n,m,s,t,dis[N][N],deg[N],to[N][N];db dp[N][N];bool vis[N],viss[N][N];vector<int>g[N];inline void addedge(int u,int v){g[u].emplace_back(v);++deg[u];}void bfs(int s){memset(vis,0,sizeof vis);queue<int>q;q.push(s);dis[s][s]=0;while(!q.empty()){int u=q.front();q.pop();if(vis[u])continue;vis[u]=true;for(auto v:g[u]){if(vis[v])continue;q.push(v);dis[s][v]=min(dis[s][v],dis[s][u]+1);}}}db dfs(int u,int v){if(viss[u][v])return dp[u][v];if(u==v)return 0;int t1=to[u][v],t2=to[t1][v];if((t1==v)||(t2==v))return 1;dp[u][v]=1;for(auto e:g[v])dp[u][v]+=dfs(t2,e)/(deg[v]+1);dp[u][v]+=dfs(t2,v)/(deg[v]+1);viss[u][v]=true;return dp[u][v];}int main(){memset(dis,0x3f,sizeof dis);memset(to,0x3f,sizeof to);scanf("%d%d%d%d",&n,&m,&s,&t);for(int i=0,u,v;i<m;i++)scanf("%d%d",&u,&v),addedge(u,v),addedge(v,u);for(int i=1;i<=n;i++)bfs(i);for(int i=1;i<=n;i++)for(auto v:g[i])for(int j=1;j<=n;j++)if(dis[i][j]-1==dis[v][j])to[i][j]=min(to[i][j],v);printf("%.3f",dfs(s,t));return 0;}

3. OSU!

题面

链接


随机一个 01 串,第 \(i\) 个字符是 \(1\) 的概率是 \(a_i\) .

求极长连续 \(1\) 段长度三次方和的期望 .

题解

喜闻乐见 osu!

\(\mathbb E^k(x)\) 表示极长连续 \(1\) 段长度 \(k\) 次方和(第 \(x\) 个是 \(1\))的期望 .

显然 \(\mathbb E^1(x)=(\mathbb E^1(x)+1)a_i\),于是可以线性递推 .

平方怎么搞?

\[\mathbb E^2(x)=\left(\mathbb E^2(x-1)+2\mathbb E^1(x-1)+1\right)a_i \]

为啥?完全平方公式 .

类似

\[\mathbb E^3(x)=\left(\mathbb E^3(x-1)+3\mathbb E^2(x-1)+3\mathbb E^1(x-1)+1\right)a_i \]

目前条件是 \(x\) 个是 \(1\),想丢掉它只需要后面加一个 \(\mathbb E^3(x-1)(1-a_i)\) 就完了 .

代码空间复杂度 \(O(n)\),滚一下可以做到 \(O(1)\) .

代码

using namespace std;const int N=100050;int n;double E1[N],E2[N],ans;int main(){scanf("%d",&n);double tmp;for(int i=1;i<=n;i++){scanf("%lf",&tmp);E1[i]=(E1[i-1]+1)*tmp;E2[i]=(E2[i-1]+2*E1[i-1]+1)*tmp;ans+=(3*E2[i-1]+3*E1[i-1]+1)*tmp;}printf("%.1f",ans);return 0;}

4. Red is good

题面

链接


\(R\)\(1\)\(B\)\(-1\),随机打乱,依次选取,可以在任意时刻停止 .

问在最优策略下期望取到数的和是多少 .

题解

dp,设答案是 \(dp_{R,B}\),则

\[dp_{R,B}=\max\left\{\dfrac{R}{R+B}(dp_{R-1,B}+1)+\dfrac{B}{R+B}(dp_{R,B-1}-1),0\right\} \]

是不是非常显然

为啥要 \(\max\{\cdots,0\}\)?因为最优策略 — 停下 .

可以滚一维,但是我不想滚(要过 DarkBZOJ 必须要滚) .

代码

using namespace std;const int N=5141;typedef long long ll;typedef double db;int r,b;db dp[N][N];int main(){scanf("%d%d",&r,&b);for(int i=1;i<=r;i++)dp[i][0]=i;for(int i=1;i<=r;i++)for(int j=1;j<=b;j++)dp[i][j]=max(0.0,1.0*j/(i+j)*(dp[i][j-1]-1)+1.0*i/(i+j)*(dp[i-1][j]+1));ll integer=floor(dp[r][b]),_=floor(dp[r][b]*1e6-integer*1e6);printf("%lld.%06lld",integer,_);return 0;}

5. 守卫者的挑战

题面

链接

题解

\(dp_{i, j, k}\) 表示前 \(i\) 场挑战赢了 \(j\) 场剩余容量为 \(k\) 的概率 .

这方案数他妈不是 \(200\times 200\times 200000=8\times 10^9\) 的吗?

容量 \(k\) 只有不大于 \(n\) 的部分是有用的,所以状态数其实是 \(8\times 10^6\) 的,可以过~鼓掌熊

有细节

代码

using namespace std;const int N=222;typedef long long ll;typedef double db;int n,l,k;struct thing{db p;int a;thing()=default;bool operator<(const thing&x){return a>x.a;}}a[N];db dp[N][N][N];int main(){scanf("%d%d%d",&n,&l,&k);for(int i=1;i<=n;i++)scanf("%lf",&a[i].p),a[i].p*=0.01;for(int i=1;i<=n;i++)scanf("%d",&a[i].a);sort(a+1,a+1+n);k=min(k,n);dp[0][0][k]=1;for(int i=1;i<=n;i++)for(int j=0;j<i;j++)for(int k=0;k<=n;k++){dp[i][j][k]+=dp[i-1][j][k]*(1-a[i].p);if(a[i].a+k>=0)dp[i][j+1][min(n,a[i].a+k)]+=dp[i-1][j][k]*a[i].p;}db ans=0;for(int j=l;j<=n;j++)for(int k=0;k<=n;k++)ans+=dp[n][j][k];printf("%.6f\n",ans);return 0;}

6. Easy

题面

链接


均匀随机 01 串,求极长连续 \(1\) 段期望和

题解

OSU! 弱化版

代码

7. 单选错位

题面

链接


均匀随机序列,第 \(i\) 位的取值是 \([1, a_i]\cap \mathbb Z\) .

循环轮换一位,问序列对应位相等个数的期望

题解

期望线性性,拆成相邻两位相同概率 .

古典概型,方案数相除,第 \(i\) 位答案就是 \(\dfrac{\min\{a_i,a_{i+1}\}}{a_ia_{i+1}}\)\(i+1\) 需要轮换 .

代码

using namespace std;const int N=1e7+500;int n,A,B,C,a[N];int main(){scanf("%d%d%d%d%d",&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;double ans=1.0/max(a[1],a[n]);for(int i=1;i<n;i++)ans+=1.0/max(a[i],a[i+1]);printf("%.3f",ans);return 0;}

8. 列队春游

题面

链接

题解

这题坑啊,连样例都没有 ... ...
唔,我好菜啊 QwQ.

Lemma 1.

\[\mathbb E(x)=\sum_{i=1}^{\infty}\mathbb P(x\ge i) \]

\(x\) 取值的期望等于它大于等于某数的概率之和 .


Proof.
由期望的定义知

\[\begin{aligned}\mathbb E(x)&=\sum_{i}i\cdot \mathbb P(x=i)\\&=\sum_{i}i(\mathbb P(x\ge i)-\mathbb P(x\ge i+1))\\&=\sum_ii\cdot\mathbb P(x\ge i)-\sum_ii\cdot \mathbb P(x\ge i+1)\\&=\sum_ii\cdot\mathbb P(x\ge i)-\sum_i(i-1)\mathbb P(x\ge i)\\&=\sum_i\mathbb P(x\ge i)\end{aligned} \]

证毕 .


Lemma 2

\[\sum_{i=1}^n\dbinom{n-i+1}{k}=\dbinom{n+1}{k+1} \]


众所周知,\(\dbinom nm=\dbinom{n-1}m+\dbinom{n-1}{m-1}\)

然后在把这个式子代到后面的 \(\dbinom{n-1}{m-1}\),可以得到

\[\dbinom nm=\sum_{i=1}^{n-1}\dbinom{i}{m-1} \]

原式 \(n-i+1\)\(i\)\(1\dots n\) 时也取\(1\dots n\),从而

\[\begin{aligned}\mathrm{LHS}&=\sum_{i=1}^n\dbinom{i}{k}\\&=\dbinom{n+1}{k+1}&=\mathrm{RHS}\end{aligned} \]

证毕 .


期望的线性性这步还是很显然的 .

算每个小朋友视野的期望,由期望的定义:

\[\mathbb E(d)=\sum_{i=1}^ni\cdot\mathbb P(d=i) \]

妈呀这不是 Lemma 1 的形式吗,直接套上

\[\mathbb E(d)=\sum_{i=1}^n\mathbb P(d\ge i) \]

然后 \(\mathbb P(d\ge i)\) 咋求 .

假设有 \(k\) 个不小于小朋友 \(i\) 身高的小朋友(这个可以轻易算出来),于是他们就会挡住小朋友 \(i\) >_<,只需要排列他们即可 .

古典概型,方案数之比,分母显然是随便放(会挡住小朋友和小朋友自己) \(A_{n}^{k+1}\) .

考虑约束:

  • 会挡住小朋友的人不能在小朋友前面 \(i-1\) 个位置上 >_<
  • 会挡住小朋友的人不能在小朋友的位置上(草)

这个方案就是 \(A_{n-i}^k\),然后小朋友有 \(n-i+1\) 种位置可以站,于是再乘一个\(n-i+1\) .

综上,有

\[\mathbb P(d\ge i) = \dfrac{(n-i+1)A_{n-i}^k}{A_n^{k+1}} \]

是不是非常简单 .

\(O(n^2)\) 不能,虽然理论可过,但是分子分母也太大了啊 炸弹熊

推一波式子吧,反正每一步都很显然 .

\[\begin{aligned}\mathbb E(d)&=\sum_{i=1}^n\mathbb P(d\ge i)\\&=\sum_{i=1}^n \dfrac{(n-i+1)A_{n-i}^k}{A_n^{k+1}}\\&=\dfrac{(n-k-1)!}{n!}\sum_{i=1}^n\dfrac{(n-i+1)!}{(n-i-k)!}\\&=\dfrac{(n-k-1)!}{n!}\cdot (k+1)!\sum_{i=1}^n\dfrac{(n-i+1)!}{(n-i-k)!(k+1)!}\\&=\dfrac{(n-k-1)!}{n!}\cdot (k+1)!\sum_{i=1}^n\dbinom{n-i+1}{k+1}\\&=\dfrac{(n-k-1)!}{n!}\cdot (k+1)!\dbinom{n+1}{k+2}\\&=\dfrac{(n-k-1)!}{n!}\cdot (k+1)!\dfrac{(n+1)!}{(k+2)!(n-k-1)!}\\&=\dfrac{n+1}{k+2}\end{aligned} \]

倒数第三个等号是 Lemma 2.

于是你大力求一遍就完了,\(O(n)\) .

代码

using namespace std;typedef double db;const int M=1234;int n,b[M],k;db ans;int main(){scanf("%d",&n);for(int i=1,x;i<=n;i++)scanf("%d",&x),++b[x];for(int i=1;i<=1000;i++){ans+=1.0*b[i]*(n+1)/(n+1-k);k+=b[i];}printf("%.2f",ans);return 0;}

9. 矩形粉刷

题面

链接


\(n\times m\) 的矩形,每次随机刷掉一个矩形,问 \(k\) 次之后期望刷掉了多少个格子

题解

期望的线性性,是个地球人都能想到

然后就变成染色一个点的概率,染一次的概率是 \(p\),染 \(k\) 次就是 \(1-(1-p)^k\) .

注意染到要讨论一大堆东西计算 /tuu

代码

using namespace std;const int N=1e5+500;typedef long long ll;typedef double db;int k,n,m;db ans=0;inline db sqr(const db&x){return x*x;}int main(){scanf("%d%d%d",&k,&n,&m);for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){db p=(sqr((i-1)*m)+sqr((j-1)*n)+sqr((n-i)*m)+sqr((m-j)*n)-sqr((i-1)*(j-1))-sqr((i-1)*(m-j))-sqr((n-i)*(j-1))-sqr((n-i)*(m-j)))/sqr(n*m);ans+=1-pow(p,k);}printf("%.0f",ans);return 0;}

10. 卡牌游戏

题面

链接

题解

每个人获胜的概率只与其在排列中与庄家的相对位置有关

\(dp(i,j)\) 为还剩 \(i\) 个人时,从庄家数第 \(j\) 个人获胜的概率 .

枚举这一次选哪张牌,可得

\[dp(i,j)=\dfrac 1m \sum_k dp(i-1, j-t+i) \]

其中 \(t\) 是死的人,即 \(t=(a_k-1)\bmod (i+1)\) .

代码

using namespace std;const int N=1e3+500;typedef long long ll;typedef double db;int k,n,m,a[N];db dp[N][N];int main(){scanf("%d%d",&n,&m);for(int i=1;i<=m;i++)scanf("%d",a+i);dp[1][1]=1;for(int i=2;i<=n;i++)for(int j=1;j<=n;j++)for(int k=1;k<=m;k++){int t=(a[k]-1)%i+1;if(t==j)continue;dp[i][j]+=dp[i-1][(j-t+i)%i]/m;}for(int i=1;i<=n;i++)printf("%.2f%% ",dp[n][i]*100);return 0;}

11. 换教室

题面

链接

题解

代码

12. 奖励关

题面

链接

题解

大水题,状压 dp .

\(dp_{k, S}\) 玩了 \(k\) 次选了 \(S\)

\[dp_{i,S}=\sum_{j=1}^n\dfrac 1n\max\{dp_{i-1, S},dp_{i-1, S\cup\{j\}} + a_j\} \]

没了 .

代码

using namespace std;const int N=16,EN=(1<<N),K=111;typedef long long ll;typedef double db;int k,n,s[N],p[N];db dp[K][EN];int main(){scanf("%d%d",&k,&n);int _=(1<<n);for(int i=1;i<=n;i++){scanf("%d",p+i);int x;while(~scanf("%d",&x)&&x)s[i]|=(1<<(x-1));}for(int i=k;i>=1;i--)for(int j=0;j<_;j++)for(int k=1;k<=n;k++){db __=dp[i+1][j];if((j&s[k])==s[k])__=max(__,dp[i+1][j|(1<<(k-1))]+p[k]);dp[i][j]+=__/n;}printf("%.6f",dp[1][0]);return 0;}

13. 概率充电器

题面

[链接]

题解

看题猜是换根但是没想出来/qd
由期望的线性性,答案就是每个点通电的概率 .

发现这个的并不太好做,我们求一下每个点不通电的概率 .

分析一下每个点受到哪些贡献:

  1. 自己通电
  2. 子树内通电
  3. 子树外通电

\(f(u)\) 为自己通电和子树内通电的概率,则

\[f(u)=(1-p_i)\prod_{v\in\mathrm{son}(u)}(f(v)+(1-f(v)(1-w)) \]

为啥?因为贡献可以拆成:

  • 子树没电
  • 子树有电,但是不通电

\(g(u)\) 表示子树外通电概率,我们可以考虑 \(u\) 的子树外到底是什么:

  • \(u\) 的父亲的子树外
  • \(u\) 的父亲的子树除了 \(u\) 的子树的部分

第一个贡献显然 .

关于第二个,因为我们是连乘积形式,所以直接除掉儿子的贡献就完了 .

于是就有 \(u\) 的子树外有电的概率:

\[P=g(fa(u))\cdot\dfrac{f(fa(u))}{f(u)+(1-f(u))(1-w)} \]

然后类似的拆贡献:

  • 子树没电
  • 子树有电,但是不通电

于是就有

\[g(u)=P+(1-P)(1-w) \]

这个从父亲到儿子的 dp 可以自顶向下做 dfs .

注意特判分母为 \(0\) 的情况

代码

using namespace std;typedef long long ll;typedef double db;const int N=514114;vector<pair<int,db>>g[N];inline void addedge(int u,int v,db w){g[u].emplace_back(make_pair(v,w));}inline void ade(int u,int v,db w){addedge(u,v,w);addedge(v,u,w);}int n;db p[N],dp1[N],dp2[N];void dfs1(int u,int fa){dp1[u]=1-p[u];for(auto e:g[u]){int v=e.first;db w=e.second;if(v==fa)continue;dfs1(v,u);dp1[u]*=dp1[v]+(1-dp1[v])*(1-w);}}void dfs2(int u,int fa){for(auto e:g[u]){int v=e.first;db w=e.second;if(v==fa)continue;if(dp1[v]+(1-dp1[v])*(1-w)==0)continue;db P=dp2[u]*dp1[u]/(dp1[v]+(1-dp1[v])*(1-w));dp2[v]=P+(1-P)*(1-w);dfs2(v,u);}}int main(){scanf("%d",&n);db w;for(int i=1,u,v;i<n;i++)scanf("%d%d%lf",&u,&v,&w),ade(u,v,w/100);for(int i=1;i<=n;i++)scanf("%lf",p+i),p[i]/=100;dp2[1]=1;dfs1(1,0);dfs2(1,0);db ans=0;for(int i=1;i<=n;i++)ans+=1-dp1[i]*dp2[i];printf("%.6f\n",ans);return 0;}

14. 游走

题面

链接

题解

考虑先统计每个点的期望经过次数 \(dp_i\),于是

\[dp_i = \sum_{i\to j}\dfrac{dp_j}{\deg j}+[i=1] \]

因为起点是 \(1\) 所以要特判 .

Gauss 消元直接解出来,然后每条边 \((u,v)\) 的期望次数就是

\[\dfrac{dp_u}{\deg u}+\dfrac{dp_v}{\deg v} \]

全部处理出来,然后排序就好了 .

代码

卡精度,我这里让 \(eps=10^{-10}\) 就过了,不用开 long double .

using namespace std;const int N=555,M=125678;const double eps=1e-10;int n,m,deg[N];double a[N][N],rk[M];vector<int>g[N];inline void addedge(int u,int v){g[u].emplace_back(v);++deg[v];}inline void ade(int u,int v){addedge(u,v);addedge(v,u);}struct Edge{int u,v;Edge(int _,int __):u(_),v(__){};Edge()=default;};vector<Edge>ed;void create(){a[1][n]=1;for(int u=1;u<n;u++){a[u][u]=1;for(auto v:g[u])if(v!=n)a[u][v]=-1.0/deg[v];}}/*此处应有 Gauss 消元*/int main(){scanf("%d%d",&n,&m);for(int i=0,u,v;i<m;i++){scanf("%d%d",&u,&v);ade(u,v);ed.push_back(Edge(u,v));}create();Gauss(n-1);int l=ed.size();for(int i=0;i<l;i++){int u=ed[i].u,v=ed[i].v;if(u!=n)rk[i+1]+=a[u][n]/deg[u];if(v!=n)rk[i+1]+=a[v][n]/deg[v];}sort(rk+1,rk+1+m);double ans=0;for(int i=1;i<=m;i++)ans+=(m-i+1)*rk[i];printf("%.3f\n",ans);return 0;}

15. XOR和路径

题面

链接

题解

由期望定义,可以拆贡献 .

xor 可以按位考虑,于是我们对每位做处理 .

\(dp_u\) 表示从 \(u\)\(n\) 路径 xor 和为 \(1\) 的概率,于是

\[dp_u=\dfrac1{\deg u}\left(\sum_{val(u,v)=0}dp_v+\sum_{val(u,v)=1}(1-dp_v)\right) \]

由于可能有后效性,于是考虑 Gauss 消元 .

做完了,注意重边自环的影响 .

时间复杂度 \(O(wn^3)\),其中 \(w\) 是边权的位数 .

代码

using namespace std;const int N=333;const double eps=1e-9;int n,m,deg[N];double a[N][N];vector<pair<int,int>>g[N];inline void addedge(int u,int v,int w){g[u].emplace_back(make_pair(v,w));++deg[v];}inline void ade(int u,int v,int w){addedge(u,v,w);addedge(v,u,w);}void create(int _){a[n][n]=1;for(int u=1;u<n;u++){a[u][u]=deg[u];for(auto e:g[u]){int v=e.first,w=e.second;if(w&(1<<_)){++a[u][v];++a[u][n+1];}else--a[u][v];}}}inline bool z(const double&x){return abs(x)<eps;}/**/int main(){scanf("%d%d",&n,&m);for(int i=0,u,v,w;i<m;i++){scanf("%d%d%d",&u,&v,&w);addedge(u,v,w);if(u!=v)addedge(v,u,w);}double ans=0;for(int i=0;i<32;i++){memset(a,0,sizeof a);create(i);Gauss();ans+=pow(2,i)*a[1][n+1];}printf("%.3f",ans);return 0;}

16. 分手是祝愿

题面

链接

题解

意识流 .

\(dp_i\) 表示从 \(i\) 个需要按的键到 \(i-1\) 个需要按的键的期望操作次数,于是

\[dp_{i}=\dfrac in + \dfrac{n-i}n(dp_i+dp_{i-1}+1) \]

代码

using namespace std;const int N=100011,P=100003;typedef long long ll;int n,k,a[N],cc;ll dp[N];void init(){for(int i=n;i>=1;i--){if(!a[i])continue;++cc;for(int j=1;j*j<=i;j++)if(!(i%j)){a[j]^=1;if(j*j!=i)a[i/j]^=1;}}}ll qpow(ll a,ll n){ll ans=1;while(n){if(n&1)ans=ans*a%P;a=a*a%P;n>>=1;}return ans;}ll inv(int x){return qpow(x,P-2)%P;}int main(){scanf("%d%d",&n,&k);for(int i=1;i<=n;i++)scanf("%d",a+i);init();for(int i=n;i>=1;i--)dp[i]=(n+(n-i)*dp[i+1]%P)%P*inv(i)%P;ll ans;if(cc<=k)ans=cc;else{ans=k;for(int i=k+1;i<=cc;i++)ans=(ans+dp[i])%P;}for(int i=1;i<=n;i++)ans=ans*i%P;printf("%lld\n",ans);return 0;}

17, 18

不会,摆烂了 .

posted @ 2022-01-22 18:49  Jijidawang  阅读(73)  评论(1编辑  收藏  举报
😅​