zoukankan      html  css  js  c++  java
  • 【BZOJ4872】[SHOI2017] 分手是祝愿(思维+动态规划)

    点此看题面

    大致题意:(n)盏灯和(n)个开关,第(i)个开关会改变所有编号为(i)的约数的灯的状态。每次随机操作,直至可以在(k)步之内使所有灯都灭掉时采取步骤最少的方案灭掉所有灯。求灭掉所有灯的期望步数乘上(n!)的值。

    前言

    少有的我能自己挖掘性质并推出来的(DP)。。。感觉就是想着想着脑袋便突然开窍了。。。

    转化

    首先,我们考虑如何求出在某一状态下至少需要几步灭掉所有灯。

    考虑我们按编号从大到小枚举每一盏灯,显然有:

    • 每个开关要么不用,要么摁一次。因为你摁得更多其实就相当于是摁了(0)(1)次,肯定不会更优。
    • 当你枚举到某一盏灯时,当且仅当它还亮着,你才能摁下开关。因为之后的灯编号比它小,肯定无法再改变它的状态,你现在的决策就相当于决定好了它最终的命运。

    也就是说,我们可以很轻松地求出至少要摁几次开关(设这一次数为(t))。

    并且根据上面第二条性质,除了这种方案以外,除非你通过摁一个开关多次来抵消自身影响,否则无论你怎么摁,都必然是非法的。

    则究竟是需要摁哪几个开关其实是没必要知道的,我们只需要知道还有(i)个开关需要摁,则就有(n-i)个开关不应摁。

    于是我们就可以考虑动态规划了。

    动态规划

    考虑我们设(f_i)表示从还有(i)个开关要摁第一次转移到还有(i-1)个开关要摁的状态的期望步数。

    则有两种可能:选择了这(i)个开关中的某一个一步成功,或是选择了剩余(n-i)个开关中的某一个转移到还有(i+1)个开关的状态,那么又得花上若干步摁回来,再继续尝试摁到(i-1)个开关的状态。

    也就是说:

    [f_i=frac in+frac{n-i}n(1+f_{i+1}+f_i) ]

    然后我们发现方程两边都有(f_i),移项得到:

    [frac inf_i=frac in+frac{n-i}n(1+f_{i+1}) ]

    接下来显然两边同时除以(frac in),得到:

    [f_i=1+frac{n-i}i(1+f_{i+1}) ]

    这样就很好(DP)了。

    计算答案

    (tle k),显然可以直接(t)步完成。

    否则,我们期望需要(f_t)步转移到(t-1)(f_{t-1})步转移到(t-2),......,(f_{k+1})步转移到(k)

    也就是(sum_{i=k+1}^tf_i+k)

    具体实现详见代码。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    #define X 100003
    #define Inc(x,y) ((x+=(y))>=X&&(x-=X))
    using namespace std;
    int n,k,a[N+5],f[N+5];
    I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
    int main()
    {
    	RI i,j,Fac=1;for(scanf("%d%d",&n,&k),i=1;i<=n;++i) scanf("%d",a+i),Fac=1LL*Fac*i%X;//读入,同时计算阶乘
    	RI t=0;for(i=n;i;--i) if(a[i]) for(++t,j=1;j*j<=i;++j) !(i%j)&&(a[j]^=1,j^(i/j)&&(a[i/j]^=1));//求出需要摁几次开关
    	if(t<=k) return printf("%d
    ",1LL*t*Fac%X),0;//如果t<=k,可以直接完成
    	for(f[n]=1,i=n-1;i>k;--i) f[i]=(1LL*(n-i)*QP(i,X-2)*(f[i+1]+1)+1)%X;//动态规划
    	RI ans=k;for(i=t;i>k;--i) Inc(ans,f[i]);return printf("%d
    ",1LL*ans*Fac%X),0;//统计答案
    }
    
  • 相关阅读:
    Mysql中表名作为参数的问题
    Mysql中时间的操作笔记
    关于ThreadAbortExcption异常处理
    数据库中判断为空后使用默认值的函数
    网页嵌入地图的方式
    常用网络CMD命令
    前端html和css
    C#查看文件目录操作、复制、替换
    网站日志统计查询工具
    SQL查看表数据占用空间代码
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ4872.html
Copyright © 2011-2022 走看看