zoukankan      html  css  js  c++  java
  • 考试T1总结(又CE?!)

    考试T1CE...

    最近不适合考试

    T1

    扶苏是个喜欢一边听古风歌一边写数学题的人,所以这道题其实是五三原题。
    歌曲中的主人公看着墙边的海棠花,想起当年他其实和自己沿着墙边种了一排海
    棠,但是如今都已枯萎,只剩下那一株,寄托着对他深深的思念。
    沿着墙一共有 n 个位置可以种下海棠花,主人公记得自己当年和他一共种下了 m
    朵,由于花的特性,海棠不能紧挨着种植,也就是两朵海棠花之间最少间隔一个不种花
    的空位置。但是她记不清当时海棠花具体是怎么摆放的了,所以她想知道一共有多少方
    案使得 m 朵海棠花都被种下且两两之间不是相邻的。我们将这 m 朵海棠花按照
    1,2,3…m 的顺序编号,两个种花的方案不同当且仅当它们被种下的位置不同或者从左向
    右数花的编号序列不同。
    为了避免输出过大,答案对一个参数 p 取模

    这道题一看就是DP嘛...

    是个数学题

    就是有n个位置,m个不同物品,问m个物品都不相邻的摆法有几种,其中m小于ceil(n/2),就是n/2上取整
    思路分析:

    思路一:

    骗分输出最小的良心防爆零样例

    思路二:

    打神搜,其实T2,T3也可以这样操作,时间不够了...只拿了十分

    代码:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    typedef unsigned int ui;
    ui type,n,m,p;
    inline ui arr(const ui &k){
        ui ans=1;
        for(ui i=2;i<=k;i++)
            ans=(ans*(i%p))%p;
        return ans;
    }
    int ans;
    inline void search(const int &step,const int &pos){
        if(step==m){
            ans++;
            return;
        }
        for(int i=2;i<=n;i++){
            if(pos+i<=n&&step+1<=m)   //没有上下界剪枝
            search(step+1,pos+i);
        }
    }
    int main(){
        freopen("ilove.in","r",stdin);
        freoprn("ilove.out","w",stdout);    //大家注意这一行的freopen!!!!!!(目力呐喊!!!)
        scanf("%d%d%d%d",&type,&n,&m,&p);
        for(int i=1;i<=n-(m-1)*2;i++) //遍历每个可能的点找可能的路线,所以非常慢,只能处理范围在20以内的点
            search(1,i);
        cout<<ans*arr(m);
        return 0;
    }

    思路三(正经思路):

    一开始的思路是考虑n个位置选m个位置再减去相邻情况,然而这并不好代码实现,而且最大数据范围是n<=2000000,m<=1000000,然而组合数最广为人知的求法要么是前缀和(杨辉三角),要么是公式计算,

    前缀和算法会因为数据范围而时间爆炸,公式计算因为取模运算的存在而正确性爆炸...

    双倍爆炸,双倍快乐,算法两开花!!!

    前缀和的爆炸原因不解释,有目共睹...

    下面讲下关于组合数取模的错误的原因:

    现给出组合数计算公式(并不想百度百科...):


    本式值即为在n个互不相同的东西中取m个的方案总数

    并且可以直观的看出该值的直接公式运算需要除法的参与,

    已知四则运算中的加减乘满足“一边算一边模还保证正确性”定律(瞎起的名字)

    除可不可以?

    那么现在考虑如下式子:

    15/21 (mod 20)

    15/7   (mod 3)

    14/6   (mod 3)

    如果把分子分母分别进行模运算再除,得到的值显然不正确,

    那么现在就要考虑乘法逆元了

    整除也不行(考虑15/5 (mod 7))

    所以要么把乘法逆元板子搬过来,要么就把运算全都变成乘运算

    现在考虑推一个保证正确的式子再将其尽可能的化简

    现有m个不同物品摆在n个位子,那么就有n-m个空位

    现在保证正确的方案就是把这几个空位固定,在它们之间插空摆物品,以保证每两个物品之间至少有一个空位

    如图:

    每两点的中间(包括最边上的两点之外那两条线)就是合法方案的可选位置

    那么就是n-m+1个位置中选择m个位置摆物品,在考虑物品的排列顺序就好了

    就是这样:

    又知:

    辣么它们的乘机化简下,就是这样滴:

    那么剩下的只有乘法运算辣!

    最后说一点,由于模数最大好像是109,所以要开long long,并且由于本题经过分析后数据范围就相当于很小了,所以不用专门写函数加上inline加速,根本没必要...

    代码(及其简短):

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    long long n,m,p;
    long long type;  //当时type为什么要开long long
    int main(){
        scanf("%d%d%d%d",&type,&n,&m,&p);   //type变量用来判别测试点类型(骗分的)
        long long ans=1;
        for(int i=n-2*m+2;i<=n-m+1;i++)
            ans=ans*i%p;
        printf("%d
    ",ans);
        return 0;
    }
  • 相关阅读:
    172. Factorial Trailing Zeroes
    96. Unique Binary Search Trees
    95. Unique Binary Search Trees II
    91. Decode Ways
    LeetCode 328 奇偶链表
    LeetCode 72 编辑距离
    LeetCode 226 翻转二叉树
    LeetCode 79单词搜索
    LeetCode 198 打家劫舍
    LeetCode 504 七进制数
  • 原文地址:https://www.cnblogs.com/648-233/p/11081005.html
Copyright © 2011-2022 走看看