Live2D

题解 分手是祝愿

whca!!!

题目传送门

Description

\(n\) 盏初始状态为 \(a_{1,2,...,n}\) 的灯,在修改一个灯的状态时它的所有因子都会跟着修改,每次随机修改一个点的状态,在把剩余灯都关掉的最小操作次数 \(\le k\) 的时候会直接采用最优方案。问期望操作次数乘上 \(n!\)

\(n\le 10^5\)

Solution

可以(bu neng) 想到的是,一个灯的影响不能通过其它灯组合(不包含该灯)来实现,也就是说一定是有些灯是必须要按的(或者说叫按奇数次),我们设有 \(s\) 个这种灯,而其它的灯按了之后必须按回来。

那我们就可以设计状态 \(f_i\) 表示现在有 \(i\) 盏需要按的灯,把需要按的灯的数量从 \(i\) 变为 \(i-1\) 的期望操作次数。可以得到转移式:

\[f_i=\frac{i}{n}+\frac{n-i}{n}\times (f_{i+1}+f_i+1) \]

\(\frac{i}{n}\) 表示直接按到需要按的灯上去了,\(\frac{n-i}{n}\) 意义则相反,\(f_{i+1}+f_{i}+1\) 表示你按错了一个灯,你就需要按回来,你还要按到 \(i-1\),还要加上你按错的这一次。

移项之后就可以得到:

\[f_i=\frac{n+(n-i)\times f_{i+1}}{i} \]

边界条件就是 \(f_{n+1}=0\),显然你不能拿 \(f_{s+1}\) 作为边界条件,因为你按错了就会变成 \(s+1\)

答案就是 \((\sum_{i=k+1}^{s} f_i+k)\times n!\)。当 \(s\le k\) 的时候直接输出 \(s\) 即可。

Code

#include <bits/stdc++.h>
using namespace std;

#define Int register int
#define MAXN 100005
#define mod 100003

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

int n,k,cnt,a[MAXN],f[MAXN];

int mul (int a,int b){return 1ll * a * b % mod;}
int dec (int a,int b){return a >= b ? a - b : a + mod - b;}
int add (int a,int b){return a + b >= mod ? a + b - mod : a + b;}
int qkpow (int a,int b){
	int res = 1;for (;b;b >>= 1,a = mul (a,a)) if (b & 1) res = mul (res,a);
	return res;
}
void Add (int &x,int y){x = add (x,y);}

void makeit (int x){
	for (Int i = 1;i * i <= x;++ i) if (x % i == 0){
		a[i] ^= 1;
		if (i * i != x) a[x / i] ^= 1;
	}
}

signed main(){
	read (n,k);
	for (Int i = 1;i <= n;++ i) read (a[i]);
	for (Int i = n;i >= 1;-- i) if (a[i]) makeit (i),++ cnt;
	for (Int i = n;i >= k;-- i) f[i] = mul (add (n,mul (n - i,f[i + 1])),qkpow (i,mod - 2));
	int ans = 0;
	if (cnt > k){
		for (Int i = cnt;i > k;-- i) Add (ans,f[i]);
		Add (ans,k);
	}
	else ans = cnt;
	for (Int i = 1;i <= n;++ i) ans = mul (ans,i);
	write (ans),putchar ('\n');
	return 0;
}
posted @ 2021-05-20 21:49  Dark_Romance  阅读(42)  评论(0编辑  收藏  举报