题目大意
有(n)盏灯和(n)个开关,初始时有的灯是亮的,有的灯是暗的。按下第(i)个开关会使第(j)盏灯的状态被改变,其中(j|i)。每次你会随机操作一个开关,直到可以通过不多于(k)次操作使所有灯都灭掉,然后按照操作次数最小的方案操作。求期望的操作次数( imes n!~mod~100003)。
(1leq nleq 100000,0leq kleq n)
题解
首先不能通过操作任意个不同的开关使得灯的状态不变,因为最大那个开关对应的灯的状态一定会改变。
所以我们每次操作亮着的灯中编号最大的那盏对应的开关,直到所有灯都灭掉。这个操作步骤一定是最优步骤。记下操作次数(num)。
设(f_i)为(i)盏灯变成(i-1)盏灯期望操作次数,有:
[egin{align}
f_i&=frac{i}{n}+frac{n-i}{n}(1+f_{i+1}+f_i)\
frac{i}{n}f_i&=1+frac{n-i}{n}f_{i+1}\
f_i&=frac{n+(n-i)f_{i+1}}{i}
end{align}
]
特殊的,(f_{n+1}=0)
最后把答案乘上(n!)
时间复杂度:(O(nsqrt n))
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
ll p=100003;
ll fp(ll a,ll b)
{
ll s=1;
while(b)
{
if(b&1)
s=s*a%p;
a=a*a%p;
b>>=1;
}
return s;
}
int a[100010];
ll f[100010];
int main()
{
int n,k;
scanf("%d%d",&n,&k);
int i,j;
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
int num=0;
for(i=n;i>=1;i--)
if(a[i])
{
num++;
for(j=1;j*j<=i;j++)
if(i%j==0)
{
a[j]^=1;
if(j*j!=i)
a[i/j]^=1;
}
}
ll s=1;
f[n+1]=1;
for(i=n;i>=1;i--)
f[i]=(n+(n-i)*f[i+1]%p)%p*fp(i,p-2)%p;
if(num<=k)
s=num;
else
{
s=0;
for(i=num;i>k;i--)
s=(s+f[i])%p;
s=(s+k)%p;
}
for(i=1;i<=n;i++)
s=s*i%p;
printf("%lld
",s);
return 0;
}