原创题目 拼方头 【组合数+记忆化优化】

题目:

有 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;
}

时间: 2024-08-03 16:06:46

原创题目 拼方头 【组合数+记忆化优化】的相关文章

POJ 1088 滑雪 记忆化优化题解

本题有人写是DP,不过和DP还是有点差别的,应该主要是记忆化 Momoization 算法. 思路就是递归,然后在递归的过程把计算的结果记录起来,以便后面使用. 很经典的搜索题目,这种方法很多题目考到的. 关键还是如何把代码写清晰工整了,O(∩_∩)O~. #include <stdio.h> const int MAX_N = 101; int R, C; int arr[MAX_N][MAX_N]; int tbl[MAX_N][MAX_N]; inline int max(int a,

【动态规划】【记忆化搜索】【搜索】CODEVS 1262 不要把球传我 2012年CCC加拿大高中生信息学奥赛

可以暴力递归求解,应该不会TLE,但是我们考虑记忆化优化. 设f(i,j)表示第i个数为j时的方案数. f(i,j)=f(1,j-1)+f(2,j-1)+……+f(i-1,j-1) (4>=j>=1),从f(n,4)开始递归求解就行. 但是考虑到状态最多只有n*4种,所以记忆化掉吧. 初始化:f(i,1)=1 (1<=i<=n-3) 1 #include<cstdio> 2 using namespace std; 3 int n; 4 long long memory

记忆化搜索+DFS URAL 1183 Brackets Sequence

题目传送门 1 /* 2 记忆化搜索+DFS:dp[i][j] 表示第i到第j个字符,最少要加多少个括号 3 dp[x][x] = 1 一定要加一个括号:dp[x][y] = 0, x > y; 4 当s[x] 与 s[y] 匹配,则搜索 (x+1, y-1); 否则在x~y-1枚举找到相匹配的括号,更新最小值 5 */ 6 #include <cstdio> 7 #include <algorithm> 8 #include <cmath> 9 #include

HDU - 6143 Killer Names(dp记忆化搜索+组合数)

题意:从m种字母中选取字母组成姓名,要求姓和名中不能有相同的字母,姓和名的长度都为n,问能组成几种不同的姓名. 分析: 1.从m种字母中选取i种组成姓,剩下m-i种组成名. 2.i种字母组成长度为n的姓-----可转换成用i种颜色给n个球染色,记忆化搜索 dfs(n,i)---用i种颜色给n个球染色的方案数 先给第1个小球涂色,有m种选择,假设涂色为color[1], 那么剩下n-1个小球: 如果继续使用color[1],则问题转化为用m种颜色给n-1个小球涂色: 如果不再使用color[1],

【记忆化搜索+优化】10411 - SKAKAVAC

[记忆化搜索+优化]10411 - SKAKAVAC Time Limit: 4000MS Memory Limit: 36000KB 给定一个N-N的矩形,每个格子有一个数字,某人最初在R行C列的位置,他可以按以下规则移动: 1.跳到相邻的行,但列数差要大于1的所有格子.跳到相邻的列,位行数差要大于1的所有格子.即如果当前位置是(r1,c1)要跳到(r2,c2)则它们满足: |r1-r2|=1且|c1-c2|>1或者|c1-c2|=1且|r1-r2|>1 2.目标格子的数字严格大于起跳格子的

【动态规划】【记忆化搜索】CODEVS 最小和 CodeVS原创

f(l,r,i)表示第i段截第l位到第r位时,当前已经得到的价格最小值,可以很显然地发现,这个是没有后效性的,因为对之后截得的段都不造成影响. 注意水彩笔数=1的特判. 递归枚举当前段的r求解(∵l是前一段的r+1),因为很多状态重复,所以可以记忆化. 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 int save[9][9][9],m,n,we

Zoj 1671 Walking Ant(BFS+优先队列||记忆化搜索)

Walking Ant Time Limit: 2 Seconds Memory Limit: 65536 KB 点击打开链接 Ants are quite diligent. They sometimes build their nests beneath flagstones. Here, an ant is walking in a rectangular area tiled with square flagstones, seeking the only hole leading to

路径方案数_mod_SPFA_记忆化搜索_C++

本文含有原创题,涉及版权利益问题,严禁转载,违者追究法律责任 本来是写个 DP 分分钟就 A 了,结果老师要我们写记忆化搜索(无奈脸) 算啦,随手一改又是一个标准的记忆化搜索(目测好像是记忆化搜索容易码一些,而且跑得快一些) 话说不取模也可以A,数据太水 很水的题吧,先 SPFA 跑一遍 2 的最短路,然后记忆化搜索统计方案 不难证明在加上最短路的限制条件后,图变成了一个 DAG 证明:首先有向是显然的,不可能存在两点 x,y,它们的最短路 d[x]>d[y] 又 d[x]<d[y] 若存在一

sicily 1219(记忆化搜索)

题目链接:sicily 1214 解题思路: 博弈题,用搜索来做.但是,如果用普通的搜索来做的话,是会超时的--复杂度大约是O( n^n ),所以需要采用记忆化搜索的方法(其实差不多就是动态规划了,但是这里是树形DP). 状态: 用集合S表示现在树的状态,i 表示现在轮到谁进行砍边,dp[ S ][ i ]表示最优值.集合S可以用二进制来表示,即001表示现在还剩下第0条边. 状态转移: 1)A的目标是取最大值,B的目标是取最小值,我们在推导当前状态的最优解时,需要分两种情况考虑!即A得维护较大