NOI将近,本蒟蒻学校名额水进了省队之后在家自学,然而年轻的我并没有意识到在家自学的学习效率近乎为0。
为了提高自己不FE的概率,所以在博客上每日更新一道dp题,达到督促自己的目的。
再加上本人是一个究极蒟蒻,所以题解自然会写的简单明了,连⑨都能明白(?)。
扯淡有点久,更新的第一道dp题就决定是省选考试的这道比较容易的期望Dp题目。
题目描述
Zeit und Raum trennen dich und mich.
时空将你我分开。
B 君在玩一个游戏,这个游戏由 n 个灯和 n 个开关组成,给定这 n 个灯的初始状态,下标为从 1 到 n 的正整数。
每个灯有两个状态亮和灭,我们用 1 来表示这个灯是亮的,用 0 表示这个灯是灭的,游戏的目标是使所有灯都灭掉。
但是当操作第 i 个开关时,所有编号为 i 的约数(包括 1 和 i)的灯的状态都会被改变,即从亮变成灭,或者是从灭变成亮。
B 君发现这个游戏很难,于是想到了这样的一个策略,每次等概率随机操作一个开关,直到所有灯都灭掉。
这个策略需要的操作次数很多, B 君想到这样的一个优化。如果当前局面,可以通过操作小于等于 k 个开关使所有灯都灭掉,那么他将不再随机,直接选择操作次数最小的操作方法(这个策略显然小于等于 k 步)操作这些开关。
B 君想知道按照这个策略(也就是先随机操作,最后小于等于 k 步,使用操作次数最小的操作方法)的操作次数的期望。
这个期望可能很大,但是 B 君发现这个期望乘以 n 的阶乘一定是整数,所以他只需要知道这个整数对 100003 取模之后的结果。
输入输出格式
输入格式:
第一行两个整数 n, k。
接下来一行 n 个整数,每个整数是 0 或者 1,其中第 i 个整数表示第 i 个灯的初始情况。
输出格式:
输出一行,为操作次数的期望乘以 n 的阶乘对 100003 取模之后的结果。
这道题的数据范围是 对于 100% 的测试点, 1 ≤ n ≤ 100000; 0 ≤ k ≤ n。
首先我们思考这样一个问题,假如可以通过k次(或以下)操作将其全变为0,那么这个操作次数是一定的,首先同一个灯不可能进行两次操作(1^1^1==1),那么对于一组灯我们暴力从后往前进行枚举,如果这个灯是1那么必然要将其灭掉(选择更小的数并不能对其造成影响),我们可以在O(sqrt(n)*n)的时间复杂度内暴力得出次数值。
这道题的数据十分的水,如果你按照以上所写的暴力直接输出期望(概率100%*次数)可以得到80分,当然如果你和我一样将所有的灯变为1的话,你就会爆0(论D2得分如何从三位数变成个位数)。
那么对于需要k次以上操作才能到达的情况,很明显如果表示每次灯的状态这是不可能做到的。但事实上我们会发现对于每个状态,我们只需要记录他到达全灭状态的次数就可以了。
显然经过每次操作到达的下一个状态一定是i+1或者i-1。
设f[i]为状态为i时的期望,那么对于i<=k时,有f[i]=i。
对于i>k,f[i]=f[i-1]
∗
/*
由期望公式
E=Σp*(E1+X1)+Σp2*(E+X2)
其中E1为下一个状态,p1和X1分别为将当前状态转移到下一个状态的概率和花费,p2和X2分别为保持当前状态的概率和花费。
以上转自http://blog.csdn.net/qq_31759205/article/details/54730101
*/
已知边界条件f[n]=f[n-1]+1,因为如果需要最大步数的时候,无论取任何操作,一定都会使操作数减少。
然而这个递推式成立的条件是f[i-1]存在,那么当k=1/k=0的时候显然无法得出结论。
那么这时候要怎么办?那么就是将递推式中存在的f[i-1]给消掉。
设g[i]=f[i]-f[i-1],则有g[i+1]=f[i+1]-f[i]。
将f[i+1]用f[i]与f[i-1]表示
g[i+1]=f[i+1]-f[i]=(f[i]-1-f[i-1]
∗
=
(f[i]−f[i−1])∗i−nn−i
=
g[i]∗i−nn−i
由此可以得出g[i]=
g[i+1]∗(n−i)+ni
同时g[n]=f[n]-f[n-1]=1,故递推可求解。
故答案为f[cnt],而此时,由于f[k]=k已知,故f[n]可以表示为
∑cntk+1g[i]
+k
综上为正解。
#include<bits/stdc++.h>
#define fer(i,j,n) for(int i=j;i<=n;i++)
#define far(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
const int maxn=100010;
const int INF=1e9+7;
const int mod=100003;
using namespace std;
/*----------------------------------------------------------------------------*/
inline ll read()
{
char ls;ll x=0,sng=1;
for(;ls<'0'||ls>'9';ls=getchar())if(ls=='-')sng=-1;
for(;ls>='0'&&ls<='9';ls=getchar())x=x*10+ls-'0';
return x*sng;
}
/*----------------------------------------------------------------------------*/
ll n,k,light[maxn],g[maxn];
ll cnt=0,ans=0;
void change(int x)
{
fer(i,1,sqrt(x))
if(x%i==0)
{
light[i]^=1;
if(i!=sqrt(x))light[x/i]^=1;
}
return ;
}
ll pw(ll x)
{
ll y=mod-2,ans=1;
for(;y;y>>=1,x=x*x%mod)
if(y&1)ans=ans*x%mod;
return ans;
}
int main()
{
n=read();k=read();
fer(i,1,n)light[i]=read();
far(i,n,1)
if(light[i]==1)
{
change(i);
cnt++;
}
if(cnt<=k)
{
for(ll i=2;i<=n;i++)
cnt=cnt*i%mod;
cout<<cnt<<endl;
return 0;
}
g[n]=1;
far(i,n-1,k+1)g[i]=(g[i+1]*(n-i)+n)*pw(i)%mod;//逆元
ans=k;
fer(i,k+1,cnt)ans+=g[i];
for(ll i=2;i<=n;i++)
ans=ans*i%mod;
cout<<ans<<endl;
return 0;
}