[SRM695-900]BearEmptyCoin

版权声明:本文是蒟蒻写出来的,神犇转载也要说一声哦! https://blog.csdn.net/WerKeyTom_FTD/article/details/80556989

题目大意

有一个两面为空的硬币,正面和反面有区别。
你需要甩k次,每次其会有一面朝上,如果该面为空,你填一个整数上去,若不为空,获得该分数。
你希望最大化分数和为s的概率。

做法

如果s mod k=0显然我们不管哪面都会填s/k,输出2k
你能决定的是第一次摇到第2个面时,根据这是第几次决定另一面填啥。设这个为y,第一次填上去的就是x,那么x是定值,你要提前准备。
下面我们令x表示正面,y表示反面(实际上由于正反面有区别,答案要乘2)。
设有A次出现正面。
那么要满足Ax+(kA)y=S
显然要有(A,kA)|S(A,k)|S
我们可以证明,存在一个x,对于满足(A,k)|SA,都存在一个对应y满足Ax+(kA)y=S
Ax+(kA)y=S
(kA)(yx)=Skx
由于AkA对称可以得到A|Skx
所以有lcm(A)|Skx
lcm(A)p+kx=S
我们知道一个简单的结论:
如果(a,c)|d,(b,c)|d([a,b],c)|d。这个结论的证明可以考虑从gcd以及lcm的本质出发,即对质因子次数取min与取max。
那么因为每个A都满足(A,k)|S于是有(lcm(A),k)|S
因此我们证明了这个x一定存在。
接下来就好做了,答案是
j=1k1maxi=0kj1[(i+j,k)|S]Ckj1i
事实上这个k开到10^5当然也是能做的。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=60+10;
ll c[maxn][maxn];
int i,j,k,l,n,m,s;
ll ans,t;
class BearEmptyCoin{
    int gcd(int a,int b){
        return b?gcd(b,a%b):a;
    }
    public:
    ll winProbability(int K,int S){
        k=K;
        s=S;
        if (s%k==0){
            ans=1;
            fo(i,1,k) ans*=2;
            return ans;
        }
        c[0][0]=1;
        fo(i,1,k){
            c[i][0]=1;
            fo(j,1,i) c[i][j]=c[i-1][j]+c[i-1][j-1];
        }
        ans=0;
        fo(j,1,k-1){
            t=0;
            fo(i,0,k-j-1)
                if (s%gcd(i+j,k)==0) t=max(t,c[k-j-1][i]);
            ans+=t;
        }
        return ans*2;
    }
} zlt;
int main(){
    printf("%lld\n",zlt.winProbability(2,-49));
}
阅读更多

没有更多推荐了,返回首页