首页 > 代码库 > 编程之美资格赛 大神与三位小伙伴

编程之美资格赛 大神与三位小伙伴

题目2 : 大神与三位小伙伴

时间限制:2000ms
单点时限:1000ms
内存限制:256MB

描述

L国是一个有着优美景色且物产丰富的国家,很多人都喜欢来这里旅游并且喜欢带走一些纪念品,大神同学也不例外。距离开L国的时间越来越近了,大神同学正在烦恼给她可爱的小伙伴们带什么纪念品好,现在摆在大神同学面前的有三类纪念品A, B, C可以选择,每类纪念品各有N种。其中种类为A_i, B_i, C_i的纪念品价值均为i, 且分别有N+1-i个剩余。现在大神同学希望在三类纪念品中各挑选一件然后赠送给她的三名可爱的小伙伴,但是她又不希望恰好挑出来两件价值相同的纪念品,因为这样拿到相同价值纪念品的两位小伙伴就会认为大神同学偏袒另一位小伙伴而不理睬她超过一星期。现在,大神同学希望你买到的三件纪念品能让三位小伙伴都开心并且不和她闹别扭,她想知道一共有多少种不同挑选的方法?

因为方案数可能非常大,大神同学希望知道挑选纪念品的方案数模10^9+7之后的答案。


输入

第一行包括一个数T,表示数据的组数。

接下来包含T组数据,每组数据一行,包括一个整数N。


输出

对于每组数据,输出一行“Case x: ”,其中x表示每组数据的编号(从1开始),后接一个数,表示模10^9+7后的选择纪念品的方案数。


数据范围

小数据:

1<=T<=10

1<=N<=100

大数据:

1<=T<=1000

1<=N<=10^18


样例解释

对于第二组数据,合法的方案有以下几种,(X,Y,Z)表示选择了A类纪念品中价值为X的,B类纪念品中价值为Y的,C类纪念品中价值为Z的。

(1,1,1): 3*3*3=27种

(1,2,3): 3*2*1=6种

(1,3,2): 3*1*2=6种

(2,1,3): 2*3*1=6种

(2,2,2): 2*2*2=8种

(2,3,1): 2*1*3=6种

(3,1,2): 1*3*2=6种

(3,2,1): 1*2*3=6种

(3,3,3): 1*1*1=1种

一共27+6+6+6+8+6+6+6+1=72种选择纪念品的方案

注意,如(1,1,2), (2,3,3), (3,1,3)都因为恰好选择了两件价值相同的纪念品,所以并不是一种符合要求的纪念品选择方法。




样例输入
2
1
3
样例输出
Case 1: 1
Case 2: 72
解法一:
由于(1,2,3)、(1,3,2)、(2,1,3)、(2,3,1)、(3,2,1)、(3,1,2)的种数都是都是一样的,可递推出N时存在只需计算其中一种组合,再x6即可算出所有排列后的种数和。代码很简单,小数据都可以轻松过,但大数据就不行了。。。
#include <stdio.h>
#define llong unsigned long long
#define MOD 1000000007

llong muti_mod(llong a,llong b)//(a*b)mod MOD
{       
     a%=MOD;
     b%=MOD;

     llong ret=0;
     while (b)//fast mul
     {
          if (b&1)//a*b=a+a(b-1)
          {
               ret+=a;//ret = (ret + a)%c
               if (ret>=MOD)
                    ret-=MOD;
          }
          //a*b=a*2 *b/2
          b>>=1;
          a<<=1;
          if (a>=MOD)
               a-=MOD;
     }
     return ret;
}

int main(void)
{
     unsigned int T=0,num=0,i=0,j=0,k=0;
     llong N=0,sum=0;

     scanf("%d",&T);

     while(num<T)
     {
          scanf("%lld",&N);
          sum=0;

         
          for (i=1;i<=N;i++)
          {
               for (j=i+1;j<=N;j++)
               {
                    #pragma omp parallel for reduction(+: sum)
                    for (k=j+1;k<=N;k++)
                    {
                         if (i!=j&&j!=k&&i!=k)//123 124 234 134
                         {
                              sum+=6*muti_mod(muti_mod(N+1-i,N+1-j),(N+1-k));
                              sum=sum%MOD;
                         }
                    }
               }
               //111 222 333
               sum+=muti_mod(muti_mod(N+1-i,N+1-i),(N+1-i));
               sum=sum%MOD;
          }
          printf("Case %d: %lld\n",++num,sum);
     }
     return 0;
}

结果如图


解法二:
考虑动态规划,算出f(n)与f(n-1)的关系,在利用数学归纳法算出f(n)的表达式:f(n) = n^2 * (n+1)^2 * (n^2-3n+4)/8。这样下来大数据也是毫秒过~~

#include <stdio.h>
#define llong unsigned long long
#define MOD 1000000007

llong muti_mod(llong a,llong b)//(a*b)mod MOD
{       
     a%=MOD;
     b%=MOD;

     llong ret=0;
     while (b)//fast mul
     {
          if (b&1)//a*b=a+a(b-1)
          {
               ret+=a;//ret = (ret + a)%c
               if (ret>=MOD)
                    ret-=MOD;
          }
          //a*b=a*2 *b/2
          b>>=1;
          a<<=1;
          if (a>=MOD)
               a-=MOD;
     }
     return ret;
}

int main(void)
{
     unsigned int T=0,num=0;
     llong N=0,sum,tmp2,tmp3,tmp4,tmp5;

     scanf("%d",&T);

     while(num<T)
     {
          sum=0;
          tmp2=0;
          tmp3=0;
          tmp4=0;
          tmp5=0;
         
          scanf("%lld",&N);
         
          #pragma omp parallel sections
          {
               #pragma omp section
               {
                    if(N&1)//
                    {
                         tmp2=muti_mod(N,N);//n^2
                         tmp3=muti_mod((N+1)>>1,(N+1)>>1);//n^3 /4
                    }
                    else
                    {
                         tmp2=muti_mod(N>>1,N>>1);//n^2 /4
                         tmp3=muti_mod(N+1,N+1);//n^3
                    }
                    tmp4=muti_mod(tmp3,tmp2);//n^3 * n^2 /4
               }
              
               #pragma omp section
               {
                    if(N<3)
                         tmp5=(4+N*N-3*N)/2;//(n^2-3N+4)/2
                    else
                    {
                         if(N&1)//
                              tmp5=muti_mod(N,(N-3)>>1)+2;//n*(n-3)/2+2
                         else
                              tmp5=muti_mod(N>>1,N-3)+2;//n/2*(n-3)+2
                    }
               }
          }    
          sum=muti_mod(tmp5,tmp4);//f(n) = n^2*(n+1)^2*(n^2-3n+4)/8

          printf("Case %d: %lld\n",++num,sum);
     }
     return 0;
}

运行结果


部分计算过程如下,我的成果啊~~