【BZOJ】4872: [Shoi2017]分手是祝愿 期望DP

【题意】给定n盏灯的01状态,操作第 i 盏灯会将所有编号为 i 的约数的灯取反。每次随机操作一盏灯直至当前状态能够在k步内全灭为止(然后直接灭),求期望步数。n,k<=10^5。

【算法】期望DP

【题解】对于当前状态,编号最大的亮灯必须通过操作自身灭掉

证明:假设通过操作编号更大的灯灭掉,那么编号更大的灯只能通过操作自己灭掉,则与原来状态无区别,得证。

运用这个结论,每次灭掉最大编号的灯后的局面中,编号最大的灯一定严格小于原最大灯,所以至多需要n次操作。

从大到小,处理出m盏待操作灯,这样一个局面就可以描述成待操作灯的数目,从而考虑期望DP。

最直观地,设f[i]表示剩余 i 盏操作灯的期望步数,根据全期望公式:

$$f[i]=\frac{i}{n}*f[i-1]+\frac{n-i}{n}*f[i+1]+1$$

等等,高斯消元?不资瓷!我们想办法变成单方向DP,去掉f[i-1]。

设f[i]表示从 i 盏待操作灯变成 i-1 盏待操作灯的期望步数,那么根据全期望公式:(省略i/n*0)

$$f[i]=\frac{n-i}{n}*(f[i+1]+f[i])+1$$

好啦!移项即可计算f[i],最后:

$$ans=\sum_{i=k+1}^{m}f[i]*n!$$

复杂度O(n√n)。

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=100010,MOD=100003;
int n,m,k,ans,a[maxn],f[maxn];
void exgcd(int a,int b,int &x,int &y){if(!b){x=1;y=0;}else{exgcd(b,a%b,y,x);y-=x*(a/b);}}
int inv(int a){int x,y;exgcd(a,MOD,x,y);return (x%MOD+MOD)%MOD;}
int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=n;i>=1;i--)if(a[i]){
        m++;
        for(int j=1;j*j<=i;j++)if(i%j==0){
            if(j*j==i)a[j]^=1;else a[j]^=1,a[i/j]^=1;
        }
    }
    for(int i=n;i>k;i--)f[i]=(n+1ll*(n-i)*f[i+1]%MOD)*inv(i)%MOD;
    if(m<=k)ans=m;else{
        for(int i=m;i>k;i--)ans=(ans+f[i])%MOD;
        ans=(ans+k)%MOD;
    }
    for(int i=1;i<=n;i++)ans=1ll*ans*i%MOD;
    printf("%d",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/onioncyc/p/8521851.html

时间: 2024-11-05 21:58:53

【BZOJ】4872: [Shoi2017]分手是祝愿 期望DP的相关文章

bzoj 4872: [Shoi2017]分手是祝愿

Description Zeit und Raum trennen dich und mich. 时空将你我分开.B 君在玩一个游戏,这个游戏由 n 个灯和 n 个开关组成,给定这 n 个灯的初始状态,下标为 从 1 到 n 的正整数.每个灯有两个状态亮和灭,我们用 1 来表示这个灯是亮的,用 0 表示这个灯是灭的,游戏 的目标是使所有灯都灭掉.但是当操作第 i 个开关时,所有编号为 i 的约数(包括 1 和 i)的灯的状态都会被 改变,即从亮变成灭,或者是从灭变成亮.B 君发现这个游戏很难,于

BZOJ4872: [Shoi2017]分手是祝愿

4872: [Shoi2017]分手是祝愿 Description Zeit und Raum trennen dich und mich. 时空将你我分开.B 君在玩一个游戏,这个游戏由 n 个灯和 n 个开关组成,给定这 n 个灯的初始状态,下标为 从 1 到 n 的正整数.每个灯有两个状态亮和灭,我们用 1 来表示这个灯是亮的,用 0 表示这个灯是灭的,游戏 的目标是使所有灯都灭掉.但是当操作第 i 个开关时,所有编号为 i 的约数(包括 1 和 i)的灯的状态都会被 改变,即从亮变成灭,

【bzoj4872】[Shoi2017]分手是祝愿 数论+期望dp

题目描述 Zeit und Raum trennen dich und mich. 时空将你我分开. B 君在玩一个游戏,这个游戏由 n 个灯和 n 个开关组成,给定这 n 个灯的初始状态,下标为从 1 到 n 的正整数.每个灯有两个状态亮和灭,我们用 1 来表示这个灯是亮的,用 0 表示这个灯是灭的,游戏的目标是使所有灯都灭掉.但是当操作第 i 个开关时,所有编号为 i 的约数(包括 1 和 i)的灯的状态都会被改变,即从亮变成灭,或者是从灭变成亮.B 君发现这个游戏很难,于是想到了这样的一个

[BZOJ 4008][HNOI2015]亚瑟王(期望Dp)

Description 小 K 不慎被 LL 邪教洗脑了,洗脑程度深到他甚至想要从亚瑟王邪教中脱坑. 他决定,在脱坑之前,最后再来打一盘亚瑟王.既然是最后一战,就一定要打得漂 亮.众所周知,亚瑟王是一个看脸的游戏,技能的发动都是看概率的.作为一个非 洲人,同时作为一个前 OIer,小 K 自然是希望最大化造成伤害的期望值.但他已 经多年没写过代码,连 Spaly都敲不对了,因此,希望你能帮帮小 K,让他感受一 下当欧洲人是怎样的体验. 本题中我们将考虑游戏的一个简化版模型. 玩家有一套卡牌,共

BZOJ 2878 [Noi2012]迷失游乐园 树形期望DP+基环树

题意:链接 方法:树形期望DP+基环树 解析: 首先先看前50%的数据 是一棵树 那么我们可以搞树形DP 然后设几个正常的数组 sum[i]代表i走i的子节点的期望的和. down[i]代表从底下走到i的期望. size[i]代表i的儿子个数 up[i]代表从i往上走的期望 然后就可以推式子了 显而易见地可以推出来up的式子 然后有一些奇怪的关于根节点的特判,注意一下就OK了. 然后后50% 我们发现它是一个基环树? 那么首先可以乱搞出来环上的点,然后记录一下这个环上的点的连接方式,求一下相邻两

[HEOI2017]分手是祝愿 期望概率dp 差分

经分析可知:I.操作每个灯可看做一种异或状态 II.每个状态可看做是一些异或状态的异或和,而且每个异或状态只能由它本身释放或放入 III.每一种异或状态只有存在不存在两中可行状态,因此这些灯只有同时处于不存在才可以,而两种异或状态之间没有关系因此可以把这些状态看做一样的,因此counts的是异或状态数. 到这里为止我们可以得到一个简单的转移方程 f[i]=i/n*f[i-1]+(n-i)/i*f[i+1]+1 于是看起来似乎已经到了解决问题的时候,所以我就开始推.......然后就没有然后了,由

BZOJ 1076 奖励关(状压期望DP)

当前得分期望=(上一轮得分期望+这一轮得分)/m dp[i,j]:第i轮拿的物品方案为j的最优得分期望 如果我们正着去做,会出现从不合法状态(比如前i个根本无法达到j这种方案),所以从后向前推 如果当前方案j里具备了取k这个物品的条件 那么dp[i,j]+=max{dp[i+1,j],dp[i+1,j  or  1<<(k?1)]+x[k]} 否则dp[i,j]+=dp[i+1,j] #include<cstdio> #include<iostream> using n

bzoj 4720: [Noip2016]换教室【期望dp】

状压dp,设f[i][j][0/1]为前i个时间段换了j间教室的期望体力消耗,转移很好想(但是写起来好长= =) #include<iostream> #include<cstdio> using namespace std; const int N=2005; int n,m,v,e,c[N],d[N]; double k[N],f[N][N][2],ans=1e9,a[N][N],z; int read() { int r=0,f=1; char p=getchar(); wh

BZOJ 3566 SHOI2014 概率充电器 树形期望DP

题目大意:给定一棵树,每个点初始有一个概率为1,为1的节点会沿着边以边权上的概率向四周扩散,求最终期望有多少个点是1 OTZ 不想写题解了贴个代码吧= = 如果有不明白做法的直接问我就好了= = #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M 500500 #define EPS 1e-7 using namespace std;