分手即是祝愿

P3750 [六省联考2017]分手即是祝愿

Zeit und Raum trennen dich und mich. 时空将你我分开。

这个题目名字不错,我非常喜欢(((

首先考虑一个开关绝不会被其他组合的开关所替代。也就是说对给定的一个组合,有几个开关是必须按的,其余的则是必须不按的。这是解题的一条关键性质。

再考虑,对当前组合我们如何求出哪些开关要按,而哪些不用按。有一个朴素的想法,从 n 开始每扫到一个亮着的开关,就对其进行操作。根据上面一条性质,我们得到了有多少个开关是必须按的。

最后我们考虑如何求期望步数。

这个就有点骚了。

所实话,我一开始是这么想的,设 dp[i] 为当前局面还有 i 个开关需要按,然后到达全灭状态的期望步数。那么有转移为

\[dp[i]=\frac{i}{n}\times dp[i-1]+1+\frac{n-i}{n}\times dp[i+1] \]

然后高斯消元即可,由于这玩意是个带状矩阵,所以可以 \(O(n)\) 消元。做法大概和 Broken Robot 相同

但是题解里面提到一种奇妙的做法

我们的状态是到达终点的状态,而他设的则是

\(dp[i]\)\(i\) 个需要按的开关到 \(i-1\) 个需要按的开关这两个局面之间的期望步数。

于是有

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

前一部分是恰好选中了那 \(i\) 个键。(这个很好理解

后一部分则是没选中那 \(i\) 个键,首先你需要 \(1\) 步来进行错误操作弄到 \(i+1\)

然后再算上 \(dp[i+1]\)\(dp[i]\) 即可。

然后化简一下就行。

(判当前步数小于 \(k\) 竟然有 \(75pts\),我很奇怪出题人怎么造的数据

#include<bits/stdc++.h>

using namespace std;

#define int long long
#define INF 1ll<<30

template<typename _T>
inline void read(_T &x)
{
	x=0;char s=getchar();int f=1;
	while(s<'0'||s>'9') {f=1;if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
	x*=f;
}
const int np = 1e5 + 5;
const int mod = 1e5 + 3;
int a[np],Ans;
int dp[np];
int fac[np];

int n , k;
inline void solve()
{
	int op = 0;
	for(int i=n;i>=1;i--)
	{
		if(a[i])
		{
			int sqt = sqrt(i);
			for(int j = 1;j<=sqt;j++)
			{
				if(i % j == 0)
				{
					a[j] ^= 1;
					if(j != i / j) a[i / j] ^= 1;
				}
			}
			Ans ++ ;
		}
	}
}

inline int power(int a,int b)
{
	int res = 1;
	while(b)
	{
		if(b & 1) res = res * a,res %= mod;
		a = a * a;
		a %= mod;
		b >>= 1; 
	}
	return res;
}

inline int inv(int i)
{
	return power(i,mod-2);
}

signed main()
{
	
	read(n);
	read(k);
	fac[0] = 1;
	for(int i=1;i<=n;i++) fac[i] = fac[i - 1] * i % mod;
	for(int i=1;i<=n;i++) read(a[i]);
	solve();
	
	dp[n] = 1;
	for(int i=n - 1;i>= k + 1;i--)
	{
		int invi = inv(i);
		dp[i] = (((((n - i) * invi)%mod) *dp[i + 1]) % mod) + ((n*invi)%mod);
		dp[i] %= mod;
	}
	if(Ans <= k)
	{
		cout<<(Ans * fac[n]) % mod;
		return 0;
	}
	int sum(0);
	for(int i=k + 1;i<=Ans;i++) sum += dp[i],sum %= mod;
	sum += k;
	sum %= mod;
	sum *= fac[n];
	sum %= mod;
	cout<<sum;
 }


总的来说,这个题的第一个性质很神仙(反正我是没想到

这个 \(dp\) 状态很神仙(虽然说用高斯消元也行

题目名字很神仙(这名字我直接吹爆,能和『谈笑风生』五五开了。

posted @ 2021-09-26 21:41  ·Iris  阅读(92)  评论(0编辑  收藏  举报