zoukankan      html  css  js  c++  java
  • 原创题目 拼方头 【组合数+记忆化优化】

    题目: 

    有 n 条木棒,现在从中选 x 根,想要组成一个正 x-1 边形,问有几
    种选法?
    由于答案较大,输出它 mod 19260817 的答案。
    万古神犇 hjr 秒了这道题,现在交给你做,好让方头开心一下。

    分析:

    此题有两个突破点:

    1、既然是用x根木棒去拼x-1边形,那么必然有且仅有一条边是由两根木棒拼成的;

    2、且其他边必然是由相同的木棒拼成的,也就是说,一种木棒仅当边数大于x-2时,才有可能成为正多边形中的边长;

    于是我们可以一 一枚举满足(2)的木棒,并再通过枚举拼凑第x-1条边;(用cnt[i]表示长度为i的边有多少根)

    首先,从cnt[i]根满足(2)的木棒中选出x-2根去拼那x-2条边,有C(cnt[i],x-2)种选择;-------♠

    其次,假设a+b=i,那么用a和b去拼第x-1条边,就有cnt[a]*cnt[b]种拼法;但若是a=b,就是C(cnt[a],2);-----------♥

    显然,♠和♥是符合乘法原理的,因此就有:f[i]=C(cnt[i],x-2)*cnt[a]*cnt[b]或是C(cnt[i],x-2)*C(cnt[a],2);

    下面是参考代码:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const long long mod=19260817;
    long long nn,x,a,maxx=0;
    long long cnt[1550]={0},p[1550],ex[1550];
    long long f[1550]={0};
    bool ok[1550];
    long long C(long long n,long long m)
    {
        if(n<m) return 0;
        if(n==m) return 1;
        if(m==1) return n%mod;
        long long c=1;
        for(long long i=1;i<=m;i++)
            c=(c*(n-i+1)/i)%mod;
        return c;
    }
    long long cal(long long i)
    {
        if(ex[i]) return ex[i];//memorizing search
        memset(p,0,sizeof(p));
        long long an=0;
        for(long long j=1;j<=min(maxx,i);j++)
        {
            if(p[j]) return an;//avoid repeated calculating
            if(j!=i-j)
            {
                an=(an+cnt[j]*cnt[i-j])%mod;
                p[j]=p[i-j]=1;
            }
            else
            {
                an=(an+C(cnt[j],2))%mod;
                p[j]=1;
            }
        }
        return ex[i]=an;
    }
    
    int main()
    {
    //    freopen("ft.in","r",stdin);
    //    freopen("ft.out","w",stdout);
        cin>>nn>>x;
        for(int i=1;i<=nn;i++)
        {
            cin>>a;
            maxx=max(maxx,a);
            cnt[a]++;
            if(cnt[a]>=x-2)
                ok[a]=1;
        }
        for(int i=1;i<=maxx;i++)
        {
            if(ok[i])//if it is possible for this edge to be the longest edge
            {
                long long a1=C(cnt[i],x-2);
                long long a2=cal(i);
                f[i]=(a1*a2)%mod;
                //pick x-2 out of cnt[i] for the longest edge
                //how many couple can make up i
                //multiply both above
            }
        }
        long long ans=0;
        for(long long i=1;i<=maxx;i++)
            ans=(ans+f[i])%mod;
        cout<<ans;
        return 0;
    }
    View Code
  • 相关阅读:
    Material Design系列第八篇——Creating Lists and Cards
    Androidの解决自动旋转导致activity重启问题
    cnBlogs博客推荐
    yii中sphinx,Ajax搜索分页
    yii框架中应用jquery表单验证插件
    百度一键分享
    yii框架中邮箱激活(数字签名)
    yii框架中保存第三方登录信息
    yii添加行的增删改查
    yii遍历行下的每列数据(小1月考)
  • 原文地址:https://www.cnblogs.com/linda-fcj/p/7206583.html
Copyright © 2011-2022 走看看