BZOJ4872 [SHOI2017]分手是祝愿

大家都很强, 可与之共勉 。

题意:
   BNNN1N10使ii1i

   BBk使kBk使BN100003

  

  
   N,k
   N01ii
   1N100000,0kN

题解:
  我萌先贪心好了,就不考虑随机,找最优解。
  考虑到操作第 i 个开关的时候,只会影响到i的,于是我萌可以知道的是,从大到小操作一定是最优的。考虑到您操作一个开关两次等于不操作,所以每一次一定是找到最大的且亮着的来操作。这样一定是最优的。复杂度为 O(nn)O(nlogn)
  然后我们来考虑如何处理期望的问题。
  首先定义 f(x) x 步后结束的期望,显然当xk时, f(x)=x (因为它已经被钦定了)。
  显然有这么个转移

f(x)=xnf(x1)+nxnf(x+1)+1

  于是就得到了一个做法。
  但是我当时脑抽想到了一个问题, mmp 要是不能初始化 f(x) 怎么办,就是一个样例( k<1 )……

  于是就改了一种定义(其实它是可以做的见万古神犇WYS

  差分定义 g(x) 为从 x 步第一次到x1步的期望,于是就可以得到转移

g(x)=xn+nxn(g(x+1)+g(x)+1)

  其中 xk||x=n 时, g(x)=1
  这样不用考虑初始化……

  于是我们把最少步数找到,然后答案累加起来。

  话说这道题完全如果给的模数不是质数,也可以做。

# include <bits/stdc++.h>

namespace In  {
# define In_Len 2000000
    static std :: streambuf *fb ( std :: cin.rdbuf ( ) ) ;
    static char buf [In_Len], *ss ( 0 ), *tt ( 0 ) ;
# define pick( )  ( (ss == tt) ? ( tt = buf + fb -> sgetn ( ss = buf, In_Len ), ((ss == tt) ? -1 : *(ss ++)) ) :*(ss ++) )

    inline char getc ( )  {  register char c ; while ( isspace ( c = pick ( ) ) ) ;  return c ;  }

    inline int read ( )  {
        register int x, c ;
        bool opt ( 1 ) ;
        while ( ! isdigit ( c = pick ( ) ) && ( c ^ -1 ) && ( c ^ 45 ) ) ;
        if ( c == 45 )  c = pick ( ), opt = 0 ;
        for ( x = -48 + c ; isdigit ( c = pick ( ) ) ; ( x *= 10 ) += c - 48 ) ;
        return opt ? x : -x ;
    }
# undef pick
# undef In_Len
}

const int N = 100010 ;

bool a [N] ;
int pool [N * 18], *iter = pool, cnt [N], cur [N], *d [N] ;

int main ( )  {

    int n, k, fac_n ( 1 ) ;

    n = In :: read ( ), k = In :: read ( ) ;

    for ( int i = 1 ; i <= n ; ++ i )  {
        for ( int j = i ; j <= n ; j += i )  {
            ++ cnt [j] ;
        }
    }
    for ( int i = 1 ; i <= n ; ++ i )  {
        d [i] = iter ;
        iter += cnt [i] ;
    }

    for ( int i = 1 ; i <= n ; ++ i )
        for ( int j = i ; j <= n ; j += i )  {
            d [j] [cur [j] ++] = i ;
        }


    for ( int i = 1 ; i <= n ; ++ i )  {
        a [i] = ( bool ) ( In :: getc ( ) - 48 ) ;
        fac_n = 1LL * fac_n * i % 100003 ;
    }

    int rt ( 0 ) ;

    for ( int i = n ; i >= 1 ; -- i )
        if ( a [i] )  {
            for ( int j = cnt [i] - 1 ; j >= 0 ; -- j )  {
                a [d [i] [j]] ^= 1 ;
            }
            ++ rt ;
        }

    if ( rt <= k )  return printf ( "%d\n", 1LL * rt * fac_n % 100003 ), 0 ;

    static int inv [N], f [N] ;

    inv [1] = 1 ;
    for ( int i = 2 ; i <= n ; ++ i ) inv [i] = 1LL * ( 100003 - 100003 / i ) * inv [100003 % i] % 100003 ;

    for ( int i = 1 ; i <= k ; ++ i ) f [i] = 1 ;

    f [n] = 1 ; 
    for ( int i = n - 1 ; i > k ; -- i ) f [i] = 1LL * ( 1LL * ( n - i ) * f [i + 1] % 100003 + n ) * inv [i] % 100003 ;

    int ans ( 0 ) ;
    for ( int i = 1 ; i <= rt ; ++ i ) ans += f [i] ;

    printf ( "%d\n", 1LL * ans * fac_n % 100003 );

    return 0 ;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值