排列组合,字符串——Killer Names

**题目:**Problem - 6143
http://acm.hdu.edu.cn/showproblem.php?pid=6143
Killer Names
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1145 Accepted Submission(s): 562

Problem Description

Galen Marek, codenamed Starkiller, was a male Human apprentice of the Sith Lord Darth Vader. A powerful Force-user who lived during the era of the Galactic Empire, Marek originated from the Wookiee home planet of Kashyyyk as the sole offspring of two Jedi Knights—Mallie and Kento Marek—who deserted the Jedi Order during the Clone Wars. Following the death of his mother, the young Marek’s father was killed in battle by Darth Vader. Though only a child, Marek possessed an exceptionally strong connection to the Force that the Dark Lord of the Sith sought to exploit.

When Marek died in 2 BBY, shortly after the formation of the Alliance, Vader endeavored to recreate his disciple by utilizing the cloning technologies of the planet Kamino. The accelerated cloning process—an enhanced version of the Kaminoan method which allowed for a rapid growth rate within its subjects—was initially imperfect and many clones were too unstable to take Marek’s place as the Dark Lord’s new apprentice. After months of failure, one particular clone impressed Vader enough for him to hope that this version might become the first success. But as with the others, he inherited Marek’s power and skills at the cost of receiving his emotions as well, a side effect of memory flashes used in the training process.

— Wookieepedia

Darth Vader is finally able to stably clone the most powerful soilder in the galaxy: the Starkiller. It is the time of the final strike to destroy the Jedi remnants hidden in every corner of the galaxy.

However, as the clone army is growing, giving them names becomes a trouble. A clone of Starkiller will be given a two-word name, a first name and a last name. Both the first name and the last name have exactly n characters, while each character is chosen from an alphabet of size m. It appears that there are m2n possible names to be used.

Though the clone process succeeded, the moods of Starkiller clones seem not quite stable. Once an unsatisfactory name is given, a clone will become unstable and will try to fight against his own master. A name is safe if and only if no character appears in both the first name and the last name.

Since no two clones can share a name, Darth Vader would like to know the maximum number of clones he is able to create.

Input

The First line of the input contains an integer T (T≤10), denoting the number of test cases.

Each test case contains two integers n and m (1≤n,m≤2000).

Output

For each test case, output one line containing the maximum number of clones Vader can create.

Output the answer mod 109+7

Sample Input

2
3 2
2 3

Sample Output

2
18

Source

2017 Multi-University Training Contest - Team 8

题意:从m个字符选出一些字符,组成姓与名。姓与名都是由n个字符组成的字符串。但是姓与名里面不能含有相同的字母。问你这样的明早由多少种。
思路:比赛的时候,其实很多自己考虑到了,但是就是ac不了,然后觉得自己是错的,很多时候,就是这样,自己怀疑自己方向错了,实际上是正确的边缘,只是有么一点点情况自己还是没有考虑到,导致思路混乱,所以吖,做题还是不够,还得加油。
方法:把问题先转化为2部分,先管姓,那么姓得问题就是。假如姓有i个字母组成。那么有i^n,但是会有重复得或者不合格得。所以要减掉。那么我们就假设一个数组f(i),f(i)的值表示,姓由i个字符组成的情况。那么
i=1时,f(1)=1;
i=2时,f(2)=2^n-c(2,1)*f(1);
i=3时,f(3)=3^n-c(3,1)*f(1)-c(3,2)*f(2);
依次推理。。
注意千万不要一步到位,认为减一次就把所有的减掉,那么很容易出错。
由于数据不多,这样,我们可以枚举前面n位有1个、2个字符组成的情况。后面的名部分就是(m-1)^n;。把前面的姓的情况,乘于后面的名部分就是这种情况的情况,然后加起来。
不懂可以看
Killer Names(HDU 6143) - 残阳止水 - CSDN博客
http://blog.csdn.net/luyehao1/article/details/77369118
这个除了理解这个,还有好多陷阱。比如,算排列组合,可以用杨辉三角去算,就是用数组存起来。记住那么公式。
还有算那个f()(下面代码里面是dp数组)的时候,我也错了好多次。
还有一个更加容易错的:所以(a-b)%mod=(a%mod-b%mod)%mod;但是在本题中,可能减的时候,会让结果变成负数,所以,最后要(dp[i]+mod)%mod;看起来多一个,其实不是,以后注意了。

附上我详细的代码:

#include
#include
#include
#include
#include
using namespace std;
#define mod 1000000007
long long int dp[2002];
long long int c[2002][2002];
long long f(long long a,long long b){
    long long sum=1;
    while(b){
        if(b&1){
            sum=(sum%mod)*(a%mod)%mod;
        }
        b/=2;
        a=a%mod*(a%mod)%mod;
    }
    return sum;
}//快速幂法。 
int main(){
    int t;
    int i,j;
    for(i=1;i<=2000;i++){
            c[i][1]=i;
            c[0][i]=0;
            c[i][0]=0;
    }

    for(i=2;i<=2000;i++){
        for(j=2;j<=i;j++){
        c[i][j]=(c[i-1][j]%mod+c[i-1][j-1]%mod)%mod;
        }
    }//记住求排列组合时,你一定用逆元了。可以用这个了。 
    scanf("%d",&t);
      long long int sum;
      long long ans;
    int n,m;
    while(t--){
        sum=0;
        scanf("%d%d",&n,&m);
        memset(dp,0,sizeof(dp));
        dp[1]=1;//这里易错,之前我直接乘于c(m,1)了。注意你这个意义。 
        for(i=2;i0;
            for(j=1;j<=i-1;j++)
            dp[i]=(dp[i]-(c[i][j]*dp[j])%mod)%mod;
            dp[i]=(dp[i]+mod)%mod;//这里也易错,减法虽然满足,取模的交换率,但是可以会变成负数,所以你加上一个不会错的。 
        }// 这里计算dp。 
        for(i=1;isum=(sum%mod+((dp[i]%mod*(c[m][i])%mod)%mod*(f(m-i,n)%mod))%mod)%mod;
        }

        printf("%lld\n",sum);
    }
    return 0;
}//求排列组合时,也可以用dp了,就不用那么麻烦去逆元了 

(本题计算那个排列组合有其他的方法,可以看看这个Hdu 6143 Killer Names【思维+斯特灵数】 - mengxiang000000 - CSDN博客
http://blog.csdn.net/mengxiang000000/article/details/77341835学点新东西)

你可能感兴趣的:(数学题,排列组合,容斥)