Codeforces Round #362 (Div. 1) C PLEASE(组合数学,指数循环节)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wust_zzwh/article/details/51965013


As we all know Barney's job is "PLEASE" and he has not much to do at work. That's why he started playing "cups and key". In this game there are three identical cups arranged in a line from left to right. Initially key to Barney's heart is under the middle cup.

Then at one turn Barney swaps the cup in the middle with any of other two cups randomly (he choses each with equal probability), so the chosen cup becomes the middle one. Game lasts n turns and Barney independently choses a cup to swap with the middle one within each turn, and the key always remains in the cup it was at the start.

After n-th turn Barney asks a girl to guess which cup contains the key. The girl points to the middle one but Barney was distracted while making turns and doesn't know if the key is under the middle cup. That's why he asked you to tell him the probability that girl guessed right.

Number n of game turns can be extremely large, that's why Barney did not give it to you. Instead he gave you an array a1, a2, ..., aksuch that

in other words, n is multiplication of all elements of the given array.

Because of precision difficulties, Barney asked you to tell him the answer as an irreducible fraction. In other words you need to find it as a fraction p / q such that , where  is the greatest common divisor. Since p and q can be extremely large, you only need to find the remainders of dividing each of them by 109 + 7.

Please note that we want  of p and q to be 1not  of their remainders after dividing by 109 + 7.

Input

The first line of input contains a single integer k (1 ≤ k ≤ 105) — the number of elements in array Barney gave you.

The second line contains k integers a1, a2, ..., ak (1 ≤ ai ≤ 1018) — the elements of the array.

Output

In the only line of output print a single string x / y where x is the remainder of dividing p by 109 + 7 and y is the remainder of dividing qby 109 + 7.

Examples
input
1
2
output
1/2
input
3
1 1 1
output
0/1

题意:有三个杯子倒扣于桌面,放的位置编号1,2,3,刚开始2号地方的杯子里有一个key,现在每次操作为从1、3号位置上等概率选择一个与2号位置的杯子交换(换杯子,位置编号还是不变,可认为左、中、右三个,比如3换到1就是右边的杯子换到中间),求n次操作后key在2号位子的杯子里的概率,结果用分数。

题解:每次都是从1、3中选一个,显然概率就是1/2,而且每次2号位置的杯子都会变化。那么dp[i][n]表示第n次操作后,key在i号位置的杯子中的概率。

显然三个杯子,i=1,2,3;把换成一维的:f1(n),f2(n),f3(n).然后递推:

对于f1(n):可能第n-1次后key就在1位置,那么第n次只能选3号,即1/2*f1(n-1);可能第n-1次后key在2号中,那么只能选1位置,即1/2*f2(n-1);只有这两个可能到达,所以f1(n)=1/2*(f1(n-1)+f2(n-1));

对于f3(n),位置是对称的:f3(n)=f1(n);

类似1,f2(n)=1/2*(f1(n-1)+f3(n-1));

我们要求的是f2(n)

上面三个等式:f2(n)=1/2*(f2(n-1)+f2(n-2));

这个式子用组合数学求解线性常系数递推式(母函数内容)

最后f2(n)=.

题目对取模要求比较变态,必须是分子分母分别取模前要求化为最简分式,最简后再分别取模。上面的式子问题在于分母的3,因为2^(n-1)与分子已经没有公因子可以消了。那么看分子里面有没有3这个因子可以消:

很巧的是:当n-1为奇数时,2^(n-1)%3=2,此时n为偶数,(-1)^n=1;所以(2^(n-1)+(-1)^n)%3=0,同理n-1位偶数时有同样的结论

也就是说分子必然能消掉3;既然能整除,那么就把(2^(n-1)+(-1)^n)/3整体当做分子,而且是可以用逆元去处理3的,这样处理后就是最简分式了

,inv是逆元,分子分母都是对1e9+7取模

现在问题就是n很大(不知道这里能用java搞),幂很大就是用指数循环节:

 

要求各欧拉函数

注意定理使用的条件,n较小时直接快速幂;

#pragma comment(linker, "/STACK:10240000,10240000")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<set>
#include<vector>
#include<map>
#include<stack>
#include<cmath>
#include<algorithm>
using namespace std;
const double R=0.5772156649015328606065120900;
const int N=1e5+5;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const double eps=1e-8;
const double pi=acos(-1.0);
typedef long long ll;
    int n;
ll a[N],ini,M;
ll judge()
{
    ll tm=mod+10;
    ll ans=1;
    for(int i=0;i<n;i++)
    {
        if(ans>tm/a[i]) return 0;//很大
        ans*=a[i];
    }
    return ans;
}
ll Pow(ll a,ll n)
{
    ll ans=1;
    while(n)
    {
        if(n&1)
            ans=ans*a%mod;
        a=a*a%mod;
        n>>=1;
    }
    return ans;
}
void solve(ll mo,ll tag)
{
    ll ans=1;
    for(int i=0;i<n;i++)
    {
        a[i]%=mo;//注意爆数据
        ans=ans*a[i]%mo;
    }
    ans--;
    ans=(ans%mo+mo)%mo;
    if(ans==0) ans+=mo;
    ll down=Pow(2,ans);
    ll up=down+tag;
    up=up*ini%mod;
    cout<<up<<'/'<<down<<endl;
}
int main()
{
     ini=333333336,M=1000000006;//事先求好逆元和欧拉值
    while(cin>>n)
    {
        int tag=-1;
        for(int i=0;i<n;i++)
        {
            scanf("%I64d",&a[i]);
            if(a[i]%2==0) tag=1;//
        }
        ll all=judge();
        if(all==0) {solve(M,tag);continue;}//幂很大
        if(all==1)
        {
            cout<<"0/1"<<endl;
            continue;
        }
        ll down=Pow(2,all-1);
        ll up=(down+tag)*ini%mod;
        cout<<up<<'/'<<down<<endl;

    }
    return 0;
}
/*

*/


猜你喜欢

转载自blog.csdn.net/wust_zzwh/article/details/51965013