FOJ 2200 cleaning(数学解法)

题目:

N个人围成一圈在讨论大扫除的事情,需要选出K个人。但是每个人与他距离为2的人存在矛盾,所以这K个人中任意两个人的距离不能为2,他们想知道共有多少种方法。

Input

第一行包含一个数T(T<=100),表示测试数据的个数。

接下来每行有两个数N,K,N表示人数,K表示需要的人数(1<=N<=1000,1<=K<=N)。

Output

输出满足题意的方案数,方案数很大,所以请输出方案数mod 1,000,000,007 后的结果。

Sample Input

2
4 2
8 3

Sample Output

4
16

思路:
现在想一想解法:
1。环形DP!!!
作者写过了,发现主要有两种方法:
1)讨论线性情况,于是发现只要讨论一下交接处即可
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define LL long long
#define mod 1000000007
using namespace std;

LL dp[1010][1010],a[10][10];

inline int read(){
    int s=0,w=1;
    char c=getchar();
    for (;!isdigit(c);c=getchar()) if (c=='-') w=-1;
    for (;isdigit(c);c=getchar()) s=(s<<1)+(s<<3)+(c^48);
    return s*w;
}

inline void begin(){
    memset(dp,0,sizeof(dp));
    dp[1][0]=dp[1][1]=dp[0][0]=1;
    dp[2][0]=1;dp[2][1]=2;dp[2][2]=1;
    dp[3][0]=1;dp[3][1]=3;dp[3][2]=2;
    for (int i=4;i<=1000;i++){
        dp[i][0]=1,dp[i][1]=i;
        for (int j=2;j<=i;j++){
            dp[i][j]=dp[i-1][j]+dp[i-3][j-1];
            dp[i][j]%=mod;
            dp[i][j]+=dp[i-4][j-2];
            dp[i][j]%=mod;
        }
    }
    a[1][1]=1;a[2][1]=1;a[2][2]=1;
    a[3][1]=3;a[3][2]=3;a[4][1]=4;
    a[4][2]=4;a[5][1]=1;a[5][2]=5;
}

int main(){
    begin();
    int n=read();
    for (int i=1;i<=n;i++){
        int x=read(),y=read();
        if (x<6){
            printf("%d\n",a[x][y]);
        }
        else{
            LL ans=0;
            ans=ans+dp[x-2][y]+dp[x-6][y-2]*3+dp[x-5][y-1]*2;
            ans%=mod;
            printf("%lld\n",ans);            
        }
    }
    return 0;
}

2)拆环:用循环队列来极解,但作者未写过。

...

2。数学解法:

相信在座的各位大佬一定学过组合数学。这题其实可以分n的奇偶性讨论,奇数时则辗转合成一条新环,再套一下公式(竞赛书里有的,在下就不推了 懒~~)。

万能母公式:

 

 

 

 但是好麻烦,要用到乘法逆元。

于是就想到了化简一波。

 

 

 so~~

还是不会呀。

但考虑到1000的巨弱数据,我们就想到了用唯一分解定理,化为指数运算,于是~~~

再用快速幂和桶就可以AC了!!!!

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define mod 1000000007
#define LL long long
using namespace std;

int a[1010],b[1010][200],fac[1010][200],cnt;

int poww(int x,int y){
    int ret=1;
    while(y){
        if(y&1) ret=1ll*ret*x%mod;
        x=1ll*x*x%mod;
        y>>=1;
    }
    return ret;
}

void Prime(int n){
    bool prime[n+10];
    memset(prime,0,sizeof(prime));
    for (int i=2;i<=n;i++){
        if (!prime[i]){
            a[++cnt]=i;
        }
        for (int j=2;j*i<=n;j++) prime[i*j]=1;
    }
    for (int i=2;i<=n;i++){
        for (int j=1;j<=cnt;j++){
            fac[i][j]=fac[i-1][j];
            if (i%a[j]==0){
                int k=i;
                while (k%a[j]==0){
                    k/=a[j],b[i][j]++,fac[i][j]++;
                }
            }
        }
            
    }
}

int get(int n,int m,int i){
    return b[n][i]+fac[n-m-1][i]-fac[n-2*m][i]-fac[m][i];
}

int main(){
    cnt=0;
    int T,n,m;
    Prime(1000);
    scanf("%d",&T);
    while (T--){
        scanf("%d%d",&n,&m);
        if (m*2>n){
            printf("0\n");
        }
        else{
            if (n%2==1){
                LL ans=1;
                for (int i=1;i<=cnt;i++){
                     int x=get(n,m,i);
                    ans*=poww(a[i],x),ans%=mod;
                }
                printf("%lld\n",ans);
            }
            if (n%2==0){
                LL ans=0;
                for (int j=0;j<=m;j++){
                    if (j>n/4||m-j>n/4) continue;
                    LL ans1=1;
                    for (int i=1;i<=cnt;i++){
                        int x=get(n/2,j,i)+get(n/2,m-j,i);
                        ans1*=poww(a[i],x),ans1%=mod;
                    } 
                    ans+=ans1,ans%=mod;
                }
                printf("%lld\n",ans);
            }
        }
        
    }
    return 0;
}

 

 
posted @ 2019-06-11 21:26  Alpha_HzH  阅读(189)  评论(0编辑  收藏  举报