• 分手即是祝愿


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

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

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

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

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

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

    这个就有点骚了。

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

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

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

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

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

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

    于是有

    [dp[i]=frac{i}{n}+(1-frac{i}{n}) imes(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) 状态很神仙(虽然说用高斯消元也行

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

  • 相关阅读:
    计算机基础
    POJO(PO)与javaBean的比较、以及DTO的说明
    Spring Assert(方法入参检测工具类-断言)
    Extjs tree的相关方法及配置项
    spring aop两种配置方式(1)
    Oracle存储过程中临时表的使用技巧
    文件上传
    java中的IO操作总结
    ExtJs文件上传(Ext.ux.form.FileUploadField)
    Extjs 属性控件[转载]
  • 原文地址:https://www.cnblogs.com/-Iris-/p/15340239.html
Copyright © 2020-2023  润新知