Codeforces 696 C. PLEASE(递推概率+数论 or 矩阵快速幂)

题目:click
题意:有三个杯子,钥匙一开始在中间,n轮游戏,每一轮游戏可以选择一个杯子与中间的交换,问n轮钥匙要中间的概率。用分数表示。
1. 有点类似概率dp的味道,但不需要你计算机跑,手算递推就可以了。
dp[i]为第i轮钥匙在中间的概率,那么dp[i]=(1-dp[i-1])/2;
很明显dp[1]=0;进行公式展开n项递推,
所得的概率为 2 n − 1 − ( − 1 ) n − 1 3 ∗ 2 n − 1 \frac{2^{n-1}-(-1)^{n-1}}{3*2^{n-1}} 32n12n1(1)n1
但n等于 ∏ \prod a[i] ,先利用欧拉定理计算 2 n − 1 2^{n-1} 2n1,
可以发现我一定需要使用快速幂中加mod,不然无法计算,这么计算我一定要找到满足条件的分子分母互质才可以如此计算。
扩展欧几里德除法可知 x>=2 x与x-1 x与x+1互质。
2 n − 1 − ( − 1 ) n − 1 2 n − 1 \frac{2^{n-1}-(-1)^{n-1}}{2^{n-1}} 2n12n1(1)n1即这个等式的分子分母互质。
gcd()=1,但是发现还有一个3并没有处理,3需要处理,判断分子与3的关系,可以证明。
n=1的时候一定成立,数学归纳法证明一下就可以了。发现 2 n − 1 − ( − 1 ) n − 1 3 \frac{2^{n-1}-(-1)^{n-1}}{3} 32n1(1)n1可以整除结果为(2*k- ( − 1 ) n (-1)^{n} (1)n)而且一定是一个奇数,但是 2 n − 1 2^{n-1} 2n1只有因子2,奇数因子并没有2,互质。完成了,1/3mod 的时候利用一下费马小定理计算。
2. 网上看到了一个巨巨使用dp加矩阵快速幂解决了,也是一个思路。
原矩阵快速幂思路:click
直接利用p/q作为基值进行递推,p[i]/q[i]=(q[i-1]-p[i-1])/(q[i-1]2);
为什么是对的?这里可以简单的证明一下,n=1的时候等于0没有问题,而每次得到的q[i]必定是一个偶数2
q[i-1]对吧,q[i-1],p[i-1]要互质那么p[i-1]必定是一个奇数,那么q[i-1]-p[i-1]仍然为奇数,奇数比一个因子只有2的偶数必互质。题目中也似乎考虑到这一点给的是0/1作为n=1的基值。
找到矩阵{{-1 1},{0,2} }。
由于解出的是p[n+1],q[n+1]之后反解。
如:(2^(n-1)*2)%mod=x 反解 2^(n-1)%mod
给出两种方法的代码:
1.

#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
#define llinf 0x3f3f3f3f3f3f3f3f
#define MAX_len 50100*4
using namespace std;
typedef long long ll;
const int mod=1e9+7;
ll a[100100];
ll dp[100100];//第i次操作钥匙在中间的概率  dp[i]=(1-dp[i-1])/2
ll quickpow(ll a,ll n)
{
    ll res=1;
    while(n)
    {
        if(n&1)
        {
            res=(res*a)%mod;
        }
        n>>=1;
        a=(a*a)%mod;
    }
    return res%mod;
}
ll gcd(ll a,ll b)
{
    if(!b)
        return a;
    else
        return gcd(b,a%b);
}
int main()
{
    ll n,i,j;
    scanf("%I64d",&n);
    ll ans=1;
    ll uuu;
    ll fh=1;
    for(i=0;i<n;i++)
    {
        scanf("%I64d",&a[i]);
        a[i]%=(mod-1);//注意a[i]的范围 防止 a[i]*ans时直接爆精度
        if(a[i]%2==0)
        {
            fh=0;
        }
        ans=((ans*a[i])%(mod-1));
    }
    ans=quickpow(2,(ans-1+mod-1));
    ll temp1=quickpow(3,mod-2);
    ll hh;
    if(fh)
        hh=-1;
    else
        hh=1;
    ll t2=(((ans+hh)%mod)*temp1)%mod;
    printf("%I64d/%I64d",t2,ans);
    return 0;
}
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
#define llinf 0x3f3f3f3f3f3f3f3f
#define MAX_len 50100*4
using namespace std;
typedef long long ll;
const int mod=1e9+7;
ll a[100100];
typedef struct A  {
	ll m[2][2];
	void Matrixclear()
    {
        m[0][0]=1;
        m[0][1]=0;
        m[1][0]=0;
        m[1][1]=1;
    }
}Matrix;
Matrix p;
Matrix matrixmul(Matrix t1,Matrix t2)
{
    Matrix temp;
    for(int i=0;i<2;i++)
    {
        for(int j=0;j<2;j++)
        {
            temp.m[i][j]=0;
            for(int k=0;k<2;k++)
            {
                temp.m[i][j]+=t2.m[k][j]*t1.m[i][k];
                temp.m[i][j]%=mod;
            }
        }
    }
    return temp;
}
Matrix quickpow(Matrix a,ll n)
{
    Matrix hh;
    hh.Matrixclear();
    while(n)
    {
        if(n&1)
        {
            hh=matrixmul(hh,a);
        }
        a=matrixmul(a,a);
        n>>=1;
    }
    return hh;
}
int main()
{
    p.m[0][0]=-1,p.m[0][1]=1,p.m[1][0]=0,p.m[1][1]=2;
    ll n,i,j;
    scanf("%I64d",&n);
    ll fh=1;
    ll yy=2;
    for(i=0;i<n;i++)
    {
        scanf("%I64d",&a[i]);
        p=quickpow(p,a[i]);
    }
    ll t1=p.m[0][1];
    ll t2=p.m[1][1];
    if(t2%2==0)
        t2/=2;
    else
        t2=(t2+mod)/2;
    ll q=(t2)%mod;
    ll p=(q-t1+mod)%mod;
    printf("%I64d/%I64d",p,q);
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值