说在前面
啊啊啊啊啊啊= =
模拟考试考了这套题,考场上推导了一个多小时终于搞出来了,然而没注意到模数是质数,以为求不出逆元…n=k的情况me又没有乘上阶乘,最后只拿到了5分
心中有100…0000万句mmp= =
题目
题面
B 君在玩一个游戏,这个游戏由
N
个灯和
输入输出格式
输入格式:
第一行两个整数
N
,
接下来一行
N
个整数,每个整数是
解法
考虑在某个局面,要把所有灯关掉的最少步数是什么
如果先关编号较小的灯,对于编号比他大的灯是没有影响的,然而在这样的策略下,关掉编号较大的灯有可能又把编号较小的灯打开了,从而浪费很多操作步数。
所以正确的策略是应该先关编号较大的灯,这样的话首先每个灯最多只会被关一次(显然)。其次就是,假设比当前灯编号大的灯都被关掉了,如果现在灯是开的,那么无论如何这个开关都必须要按下1次(奇数次),才能把这个灯关掉(因为关掉它的倍数的灯的时候总会影响到它)
同时可以发现,那些需要被摁下的开关都得去摁下1次(奇数次),所以与摁下的顺序无关(可以用异或关系理解)
具体的做法
因此可以先从大到小用 根号枚举约数 的方式,计算出初始局面关掉所有灯的最小步数,如果这个数小于等于
K
就直接输出了(这样的数据有
现在设出
f[i]
表示在最优策略下还需要摁下
i
个开关才能关掉所有灯,明显有
根据最后一个条件带回到f[N-1]的式子里,发现 f[N-2]也可以用f[N-1]表示出来。一直倒推回去最后就可以求出 f[K]关于f[N-1]的表达式,然后同样可以得到 f[当前最优步数]关于f[N-1]的表达式,然后就可以求出答案了。
令f[N-1]为x,则有f[i]=x+Q,f[i+1]=x+R,带入f[i]的式子,那么有
f[i−1]=x+−(N−i)∗R−N+NQi
,分母就是乘上逆元。最后把期望乘上阶乘即为答案
至于为什么得到的每个f[i]都可以表示成 x+常数 的形式 ,me写了一页草稿纸= =,有兴趣的可以自己推导。像这种一项与相邻两项有关系的情况,一般方法是两项差分,也就是设 b[i]=f[i]−f[i−1] ,有 b[i]=in+(1−in)⋅(1+b[i+1]+b[i]) ,边界 b[i]=1 ,倒推即可,答案就是b[K+1]到b[当前最优步数]之和
下面是自带大常数的代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
int N , K , a[100005] , cnt ;
int fx[100005] , fac[100005] , inv[100005] , mmod = 100003 ;
void preWork(){
for( int i = N , j ; i >= 1 ; i -- )
if( a[i] ){
for( j = 1 , cnt ++ ; j * j < i ; j ++ )
if( i % j == 0 ) a[j] ^= 1 , a[i/j] ^= 1 ;
if( j * j == i ) a[j] ^= 1 ;
}
fx[N] = N ; fac[0] = fac[1] = 1 ;
for( int i = N - 1 ; i >= 1 ; i -- )
fx[i] = 1LL * fx[i+1] * i % mmod ;
for( int i = 2 ; i <= N ; i ++ )
fac[i] = 1LL * fac[i-1] * i %mmod ;
inv[1] = 1 ;
for( int i = 2 ; i <= N ; i ++ ){
inv[i] = -1LL * ( mmod / i ) * inv[ mmod%i ] %mmod ;
inv[i] = ( inv[i] %mmod + mmod )%mmod ;
}
}
void solve(){
if( cnt <= K ){
printf( "%d" , 1LL * cnt * fac[N] %mmod ) ;
return ;
}
long long Rk = 0 , Rcnt = 0 , ans , Q = 0 , R = 1 ;
for( int i = N - 1 ; i > K ; i -- ){
Rk = ( -1LL * N * R %mmod + i * R %mmod - N ) * inv[i] ;
Rk = ( Rk %mmod + N * Q %mmod * inv[i] %mmod ) ;
Rk = ( Rk %mmod + mmod )%mmod ;
R = Q ; Q = Rk ;
}
if( cnt == N ) Rcnt = 1 ;
Q = 0 , R = 1 ;
for( int i = N - 1 ; i > cnt ; i -- ){
Rcnt = ( -1LL * N * R %mmod + i * R %mmod - N ) * inv[i] ;
Rcnt = ( Rcnt %mmod + N * Q %mmod * inv[i] %mmod ) ;
Rcnt = ( Rcnt %mmod + mmod )%mmod ;
R = Q ; Q = Rcnt ;
}
ans = ( K - Rk + Rcnt ) * fac[N] ;
printf( "%lld" , ( ans %mmod + mmod )%mmod ) ;
}
int main(){
// freopen( "trennen.in" , "r" , stdin ) ;
// freopen( "trennen.out", "w" , stdout) ;
scanf( "%d%d" , &N , &K ) ;
for( int i = 1 ; i <= N ; i ++ )
scanf( "%d" , &a[i] ) ;
preWork() ;
solve() ;
}