五一蹲大牢 叁

T1 数列 Sequence
题目描述
我们称一个长度为 2n 的数列是有趣的,当且仅当该数列满足以下三个条件:

(1)它是从 1 到 2n 共 2n个整数的一个排列{ai};

(2)所有的奇数项满足 a1<a3<…<a2n-1,所有的偶数项满足 a2<a4<…<a2n

(3)任意相邻的两项 a2i-1与 a2i(1≤i≤n)满足奇数项小于偶数项,即:a2i-1<a2i。  现在的任务是:对于给定的n,请求出有多少个不同的长度为 2n 的有趣的数列。因为 最后的答案可能很大,所以只要求输出答案 mod P 的值。

输入格式
输入文件只包含用空格隔开的两个整数 n 和 P。

输出格式 
仅含一个整数,表示不同的长度为 2n 的有趣的数列个数 mod P 的值。

样例输入 
3 6 

样例输出 
 5 

数据范围与约定 
对于 30%的数据满足 n≤1000; 

对于另外 30%的数据满足 P 为质数; 

对于100%的数据满足 n≤1000000 且 P≤1000000000。

洛谷题号P3200

把前几个n的答案用暴力打出来,发现是卡特兰常数,但是P不是质数,所以可能没有逆元,所以用式子求,求组合数时就,考虑质因数分解n!,因为n!中有(n/p)+(n/p2)+(n/p3)+.....,所以实现起来很方便

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
int pri[10000005],vis[10000005],tot=0;
int p;
int calc(int m,int x){
    int o=x,res=0;
    while(m>=o){
        res+=m/o;
        o*=x;
    }
    return res;
}
int kkk(int a,int b){
    int res=1;
    while(b){
        if(b&1)res=res*a%p;
        a=a*a%p;
        b>>=1;
    }
    return res;
}
int C(int m,int n){
    int res=1;
    for(int i=1;pri[i]<=n;i++){
        int x=calc(n,pri[i]),y=calc(m,pri[i]),z=calc(n-m,pri[i]);
        x-=z+y;
        res=res*kkk(pri[i],x)%p;
    }
    return res;
}
int n;
signed main(){
    freopen("sequence.in","r",stdin);
    freopen("sequence.out","w",stdout);
    scanf("%I64d%I64d",&n,&p);
    for(int i=2;i<=10000000;i++){
        if(!vis[i])vis[i]=i,pri[++tot]=i;
        for(int j=1;j<=tot&&pri[j]*i<=10000000;j++){
            vis[pri[j]*i]=pri[j];
            if(i%pri[j]==0)break;
        }
    }
    int x=C(n,2*n),y=C(n-1,2*n);
    printf("%I64d",((x-y)%p+p)%p);
    return 0;
}

T2 乘法 Mul
题目描述

输入一个 n ∗ n 的矩阵 A,请求出对 m 取模的结果。

输入格式

第一行为三个正整数 n, k, m,含义如上所述;

接下来 n 行,每行输入 n 个非负整数,用于描述矩阵 A。

输出格式

输出 n 行,每行 n 个整数,表示答案矩阵。

样例输入

2 2 5

2 1

0 3

样例输出

1 1

0 2

数据范围与约定

对于前 30% 的数据,k<=30;

对于另外 30% 的数据,n=1;

对于 100%的数据,n<=30, k<=1e9 , m<=1e4,输入矩阵的数在 0~m-1 范围内

令s[i]=a1+a2+a3+...+a2^i,由于s[i]=s[i-1]+a2^(i-1),所以可以在快速幂时递推求出s[i],由于任意一个数字可以用二进制表示,如6=(110)2,及ans=s[3]+s[2]*a2^(3),所以可以把k拆成长度为2的整数次方的小段,统计每一小段即可。

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
int m;
int n,k;
struct node{
    int a[35][35];
    void clear(){
        memset(a,0,sizeof(a));
    }
    node operator +(node x){
        node c;
        c.clear();
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                c.a[i][j]=a[i][j]+x.a[i][j];
                c.a[i][j]%=m;
            }
        }
        return c;
    }
    node operator *(node x){
        node c;
        c.clear();
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                int res=0;
                for(int k=1;k<=n;k++)res=res+a[i][k]*x.a[k][j]%m;
                c.a[i][j]=res%m;
            }
        }
        return c;
    }
    void print(){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                printf("%I64d ",a[i][j]);
            }
            puts("");
        }
    }
}s[55],pw[55];
node o,ans;
signed main(){
    freopen("mul.in","r",stdin);
    freopen("mul.out","w",stdout);
    scanf("%I64d%I64d%I64d",&n,&k,&m);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            scanf("%I64d",&o.a[i][j]);
        }
    }
    int b=k,tot=1;
    ans.clear();
    s[0]=o;
    tot=0;
    while(b){
        s[tot+1]=o*s[tot]+s[tot];
    /*    s[tot].print();*/
        pw[tot]=o;
        o=o*o;
        tot++;
        b>>=1;
    }
    pw[tot]=o;
    o.clear();
    for(int i=1;i<=n;i++)o.a[i][i]=1;
    for(int i=31;i>=0;i--){
        if(k&(1<<i)){
            ans=ans+o*s[i];
            /*ans.print();
            puts("-------------------");*/
            o=o*pw[i];
        }
    }
    ans.print();
    return 0;
}

T3 生物 Creature

题目描述

在一个无限长的一维空间中,存在着一个奇特的生物,它的身体上顺次有着 n + 1 个 刻印,每个刻印可以用一个正整数来表示。已知它最后一个刻印的值为 m,而其它 n 个刻 印的值均不超过 m,并且两个刻印的值可以相同。 这个生物每次可以选中它的任意一个刻印,并且按照这个刻印的值 k,选择向它所在位 置的前或后闪烁 k 个单位。我们称可以使得这个生物能够通过若干次闪烁,到达一维空间 任何一个位置的刻印序列为超刻印序列。 现在刻印序列显然一共有 m n 种,为了研究这个生物,请你求出其中超刻印序列的数 目。

输入格式

仅一行两个整数,分别为 n, m。

输出格式

输出一行一个整数,表示超刻印序列的数目对 1e9 +7 取模的结果。

样例输入

2 4

样例输出

12

数据范围与约定

对于前 20%的数据,保证 n,m <= 5;

对于 100%的数据,保证 1<=n<=15,1<=m<=1e8。

这道题就是要找满足gcd等于1的序列数,证明见裴蜀定理。正难则反,用总数减去不满足的,因为最后一个为m,先分解m的质因数,若数列每个数都存在m的某个质因数那么就不满足,而1~m中满足此条件的有m/“该质数”个,由于有n个位置,所以对于每个质数有(m/“该质数”)n种,但是考虑这样会算重,如6会被2给算一次,又会被3算一次,所以考虑用容斥原理来消除重复的部分。

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
int pri[100005],vis[100005],tot=0;
int p[20],cnt=0,ans=0,mod=1e9+7,s[20],tp=0;
int kkk(int a,int b){
    int res=1;
    while(b){
        if(b&1)res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
void dfs(int k,int lst,int S,int op){
    if(k==S+1){
        int res=1;
        for(int i=1;i<=S;i++)res*=s[i];
        ans=((ans+kkk(m/res,n)*op)%mod+mod)%mod;
        return;
    }
    for(int i=lst;i<=cnt;i++){
        s[++tp]=p[i];
        dfs(k+1,i+1,S,op);
        --tp;
    }
}
signed main(){
    freopen("creature.in","r",stdin);
    freopen("creature.out","w",stdout);
    for(int i=2;i<=100000;i++){
        if(!vis[i])vis[i]=i,pri[++tot]=i;
        for(int j=1;j<=tot&&pri[j]*i<=100000;j++){
            vis[pri[j]*i]=pri[j];
            if(i%pri[j]==0)break;
        }
    }
    scanf("%I64d%I64d",&n,&m);
    int o=m;
    for(int i=1;i<=tot&&pri[i]<=o;i++){
        if(o%pri[i]==0){
            p[++cnt]=pri[i];
            while(o%pri[i]==0)o/=pri[i];
        }
    }
    if(o!=1)p[++cnt]=o;
    for(int i=1,op=1;i<=cnt;i++,op*=-1){
        tp=0;
        dfs(1,1,i,op);
    }
    printf("%I64d",((kkk(m,n)-ans)%mod+mod)%mod);
    return 0;
}

------2022.5.6

posted @ 2022-05-06 17:07  夏天海里的余  阅读(38)  评论(0)    收藏  举报