• 考试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;
    }
  • 相关阅读:
    虚拟机的Linux 安装 若干问题(一)
    理解JavaScript的闭包
    javascript里面的引用类型和值类型
    javascript导入自定义模块
    简单了解下CAP定理与BASE定理
    背包问题之完全背包
    背包问题之多重背包
    背包问题之0-1背包
    搜索算法初步总结
    谈一谈“回溯法“
  • 原文地址:https://www.cnblogs.com/648-233/p/11081005.html
Copyright © 2020-2023  润新知