原题传送门
这是一道期望DP
首先根据题意,我们需要求出所需最小步数,这是可以求的
方法:从n到1枚举,如果灯开着,就关掉,顺便把因数都反转一下状态(xor),这是正确的,感性理解一下喽
设最小步数已经求出来了,记为
s
u
m
sum
sum
接下来就是根据题意讨论:
- s u m < = k , a n s = s u m sum<=k,ans=sum sum<=k,ans=sum
-
s
u
m
>
k
,
用
到
D
P
sum>k,用到DP
sum>k,用到DP
令 d p [ i ] 表 示 还 需 要 按 i 次 到 需 要 按 ( i − 1 ) 次 的 期 望 步 数 dp[i]表示还需要按i次到需要按(i-1)次的期望步数 dp[i]表示还需要按i次到需要按(i−1)次的期望步数
可得方程: d p [ i ] = i n ∗ 1 + ( 1 − i n ) ∗ ( d p [ i + 1 ] + d p [ i ] + 1 ) dp[i]=\frac{i}{n}*1+(1-\frac{i}{n})*(dp[i+1]+dp[i]+1) dp[i]=ni∗1+(1−ni)∗(dp[i+1]+dp[i]+1)
意义:有 i n \frac{i}{n} ni的概率按对,步数为1;有 ( 1 − i n ) (1-\frac{i}{n}) (1−ni)的概率按错,导致变成还需按 ( i + 1 ) (i+1) (i+1)次,步数为 d p [ i + 1 ] + d p [ i ] + 1 dp[i+1]+dp[i]+1 dp[i+1]+dp[i]+1
化简得真·转移方程: d p [ i ] = n + ( n − i ) ∗ d p [ i + 1 ] i , 除 以 i 则 是 用 逆 元 解 决 dp[i]=\frac{n+(n-i)*dp[i+1]}{i},除以i则是用逆元解决 dp[i]=in+(n−i)∗dp[i+1],除以i则是用逆元解决
答案统计则是如此: a n s = k + ∑ i = k + 1 s u m d p [ i ] ans=k+\sum_{i=k+1}^{sum}dp[i] ans=k+∑i=k+1sumdp[i]
最终不能忘了
∗
n
!
*n!
∗n!哟
Code:
#include <bits/stdc++.h>
#define maxn 100010
#define LL long long
#define qy 100003
using namespace std;
int n, k, a[maxn], inv[maxn], dp[maxn], sum, ans;
vector <int> s[maxn];
inline int read(){
int s = 0, w = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
return s * w;
}
int ksm(int n, int k){
if (!k) return 1;
int sum = ksm(n, k >> 1);
sum = 1LL * sum * sum % qy;
if (k & 1) sum = 1LL * sum * n % qy;
return sum;
}
int main(){
n = read(), k = read();
for (int i = 1; i <= n; ++i) a[i] = read();
for (int i = 1; i <= n; ++i) inv[i] = ksm(i, qy - 2);
for (int i = 1; i <= n; ++i)
for (int j = i; j <= n; j += i) s[j].push_back(i);
for (int i = n; i; --i)
if (a[i]){
++sum;
for (int j = 0; j < s[i].size(); ++j) a[s[i][j]] ^= 1;
}
if (sum <= k) ans = sum % qy; else{
dp[n] = 1;
for (int i = n - 1; i > k; --i)
dp[i] = 1LL * inv[i] * (n + 1LL * (n - i) * dp[i + 1] % qy) % qy;
for (int i = sum; i > k; --i) (ans += dp[i]) %= qy;
(ans += k) %= qy;
}
for (int i = 1; i <= n; ++i) ans = 1LL * ans * i % qy;
printf("%d\n", ans);
return 0;
}
终于肝完了,睡觉去了~~

博客是一道期望DP题解。先从n到1枚举求所需最小步数sum,若sum<=k,答案为sum;若sum>k则用DP。令dp[i]表示还需按i次到(i - 1)次的期望步数,得出转移方程,答案统计后最终结果要乘n! 。
693

被折叠的 条评论
为什么被折叠?



