acm专题学习之数学(二)康托展开+CodeChef - STFM

题意:给你一个公式F(x) = 1 × (1! + x) + 2 × (2! + x) + · · · + x × (x! + x),n个pi,计算F(p1) + F(p2) + · · · + F(pn),然后结果模m。

康拓展开:X=a[n]∗(n−1)!+a[n−1]∗(n−2)!+...+a[i]∗(i−1)!+...+a[1]∗0!

含义:设有n个数(1,2,3,4,…,n),可以有组成不同(n!种)的排列组合,康托展开表示的就是是当前排列组合在n个不同元素的全排列中的名次。

 a[i]的含义:a[i]为整数,并且0 <= a[i] <= i, 0 <= i < n, 表示当前未出现的的元素中排第几个(是从0开始算的)。例如:3412,a[3]=2,a[4]=2,a[1]=0,a[2]=0。

特别:对于一个序列来说,例如3个数(1,2,3)来说,会发现序列(1 2 3)的排名为1,序列(3 2 1)的排名为3!。序列(1,2,· · ·,n)排名为0,序列(n,n-1,· · ·,1)的排名为n!-1(因为计算的排名是从0开始算的所以要-1)。

思路:把F(x) = 1 × (1! + x) + 2 × (2! + x) + · · · + x × (x! + x)转换成F(x)=1∗1!+2∗2!+3∗3!+ · · · +x∗x!+(1+2+3 +· · · +x)∗x,然后就会发现前面的式子1∗1!+2∗2!+3∗3!+ · · · +x∗x!其实是序列(x+1 x x-1 · · ·1)的康拓展开式,会发现它是最后一名。因为计算的排名是从0开始算的,所以上述的序列的康拓展开排名为(x+1)!-1。于是F(x)的函数表达就转换成F(x)=(x+1)!−1+(1+x)*x*x/2了。

还有就是关于大阶乘取模的问题,如果n>=mod,那么n的阶乘的因子中一定有mod,n的阶乘模mod一定等于0。

代码:

#include <algorithm>
#include <iostream>
#include <string>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn=1e5+5;
const int maxm=1e7+10;
int n,mod;
long long int num[maxn];//记录数字
long long int sum[maxm];//记录阶乘
int main(){
    scanf("%d%d",&n,&mod);
    for(int i=0;i<n;i++){
        scanf("%lld",&num[i]);
    }
    sum[1]=1;
    for(int i=2;i<=mod;i++){//计算阶乘
        sum[i]=(i*sum[i-1])%mod;
    }
    long long int ans=0;
    for(int i=0;i<n;i++){
        long long int tmp=0;
        //分奇数偶数是因为要除2
        if(num[i]%2){//奇数
            tmp=(num[i]+1)/2%mod*(num[i]%mod)%mod*(num[i]%mod)%mod;
        }
        else {//偶数
            tmp=num[i]/2%mod*(num[i]%mod)%mod*((num[i]+1)%mod)%mod;
        }
        if(num[i]+1<mod)
            tmp=(tmp+sum[num[i]+1])%mod;
        ans=(ans+tmp+mod-1)%mod;
    }
    printf("%lld\n",ans);
    return 0;
}

总结:学习了一下大数阶乘取模,还有求和奇数偶数处理,和康拓展开。平时还是要多做点题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值