[SHOI2017]分手是祝愿

题目:
https://ac.nowcoder.com/acm/problem/20437

n n n盏灯, 1 1 1表示开的, 0 0 0表示关的。每次操作随机选一个开关 i ( 1 ≤ i ≤ n ) i(1\le i\le n) i(1in),对于所有的 j ( j ∣ i ) j(j|i) j(ji),第 j j j盏灯的状态反转。如果当前在最优的情况下只需要 x ( x ≤ k ) x(x\le k) x(xk)个开关就能使所有的灯关掉,则按照最优的情况进行操作。求关掉所有灯的期望次数。

思路:

  • 首先考虑如何对于一个状态求最小步数。
    由于编号小的开关不会影响到后面的灯,所以应该按编号从大到小决策。对于当前枚举到的灯 i i i,如果它是开着的,那么必须使用开关 i i i。所以可以很快求出对于初始状态的步数。
  1. 最优情况每个开关最多操作一次,所以最优情况最多为 n n n
  2. 最优操作的顺序没有关系。
  3. 最优操作唯一(不考虑顺序)
  • d p [ i ] dp[i] dp[i]表示在最优情况下还需要 i i i次操作才能把所有灯关掉的状态到完成态的期望操作次数。(因为关掉一盏灯会影响还需要的最优步数)那么接下来有 i n \frac{i}{n} ni的概率到达最优情况下还要 i − 1 i-1 i1次操作的状态, n − i n \frac{n-i}{n} nni的概率到达 i + 1 i+1 i+1的状态。(如果使用的就是最优策略的 i i i个开关中的一个,那么最优次数减 1 1 1,否则加 1 1 1)
    d p [ i ] = { i n ⋅ d p [ i − 1 ] + n − i n ⋅ d p [ i + 1 ] + 1 ( k < i ≤ n ) ① i ( i ≤ k ) ② dp[i]= \begin{cases} \frac{i}{n}\cdot dp[i-1]+\frac{n-i}{n}\cdot dp[i+1]+1(k<i\le n)\quad①\\ i(i\le k)\quad②\\ \end{cases} dp[i]={nidp[i1]+nnidp[i+1]+1(k<in)i(ik)
    设初始最优要 n u m num num次,则 d p [ 0 ] = 0 dp[0]=0 dp[0]=0 d p [ n u m ] dp[num] dp[num]为答案。
  1. 解方程组,时间复杂度太高
  2. 看成数列,构造
    d p [ i ] = i n ⋅ d p [ i − 1 ] + n − i n ⋅ d p [ i + 1 ] + 1 i n ⋅ ( d p [ i ] − d p [ i − 1 ] ) = n − i n ⋅ ( d p [ i + 1 ] − d p [ i ] ) + 1 令 d p [ i ] − d p [ i − 1 ] = f [ i ] f [ i ] = n − i i ⋅ f [ i + 1 ] + n i f [ n ] = 1 k < i ≤ n \begin{aligned} dp[i]&=\frac{i}{n}\cdot dp[i-1]+\frac{n-i}{n}\cdot dp[i+1]+1\quad\\ \frac{i}{n}\cdot(dp[i]-dp[i-1])&=\frac{n-i}{n}\cdot(dp[i+1]-dp[i])+1\quad令dp[i]-dp[i-1]=f[i]\\ f[i]&=\frac{n-i}{i}\cdot f[i+1]+\frac{n}{i}\quad f[n]=1\quad k<i\le n\\ \end{aligned} dp[i]ni(dp[i]dp[i1])f[i]=nidp[i1]+nnidp[i+1]+1=nni(dp[i+1]dp[i])+1dp[i]dp[i1]=f[i]=inif[i+1]+inf[n]=1k<in
    从后往前递推 f [ i ] f[i] f[i]。如果 n u m > k , d p [ n u m ] = d p [ k ] + ∑ i = k + 1 n u m f [ i ] num>k,dp[num]=dp[k]+\sum_{i=k+1}^{num}f[i] num>k,dp[num]=dp[k]+i=k+1numf[i]
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N=100005,mo=100003;

typedef long long LL;

int n,Fac[N],C[N],m,k,bz[N],Inv[N],ans;

char c;

int read()
{
    int x=0,sig=1;
    for (c=getchar();c<'0' || c>'9';c=getchar()) if (c=='-') sig=-1;
    for (;c>='0' && c<='9';c=getchar()) x=x*10+c-48;
    return x*sig;
}

int main()
{
    n=read(); k=read();
    for (int i=1;i<=n;i++) bz[i]=read();
    for (int i=n;i;i--) if (bz[i])
    {
        m++;
        for (int j=1;j*j<=i;j++) if (i%j==0)
        {
            bz[j]^=1;
            if (i/j>j) bz[i/j]^=1;
        }
    }
    Fac[0]=Fac[1]=Inv[1]=1;
    for (int i=2;i<=n;i++)
    {
        Fac[i]=(LL)Fac[i-1]*i%mo;
        Inv[i]=(LL)Inv[mo%i]*(mo-mo/i)%mo;
    }
    if (k>=m)
    {
        printf("%d\n",(LL)Fac[n]*m%mo);
        return 0;
    }
    C[0]=1;
    for (int i=0;i<n;i++) C[i+1]=(LL)C[i]*(n-i)%mo*Inv[i+1]%mo;
    for (int i=1;i<=n;i++) C[i]=(C[i]+C[i-1])%mo;
    for (int i=k+1;i<=m;i++) ans=(ans+(LL)Fac[n-i]*Fac[i-1]%mo*(C[n]-C[i-1]))%mo;
    if (ans<0) ans+=mo;
    ans=(LL)ans*n%mo;
    ans=(ans+(LL)Fac[n]*k)%mo;
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值