(2012-04-24 21:38:48)
SOJ3082:http://cstest.scu.edu.cn/soj/problem.action?id=3082
这道题的题意是说,给出一排长度为n的格子,现在使用m种颜色对n个格子涂色。要求:相邻的格子不同色并且m种颜色都要使用。给出n,m,求涂色的方案数。
刚开始我考虑用递推公式来做,但是推不出来而且n的范围是10^9,m的范围是10^3也不能用表来存。后来看了题目分类是容斥原理。
解题如下:
其实可以这样考虑:将n个物品放入m个箱子中,任一个箱子不能为空而且两个相邻的物品不能放入同一个箱子。设总数S表示不存在相邻的两个物品放入同一个箱子,集合表示第i个箱子为空。则所求即为,其中
整理得,result=
由于m只有1000,所以只需算出每个模19871118再求和即可。
代码如下:
#include<iostream>
#include<cstring>
using namespace std;
#define MAX 105
#define BASE 10000
int a[505][1005];//存放C(m,k)
long long mod[1005];//存放C(m,k)mod19871118
void multiply(int a[],int max,int b)
{
int i,array=0;
for (i = max-1; i >= 0; i--)
{
array += b * a[i];
a[i] = array % BASE;
array /= BASE;
}
}
void divide(int a[], int max, int b)
{
int i, div = 0;
for (i = 0; i < max; i++)
{
div = div * BASE + a[i];
a[i] = div / b;
div %= b;
}
}
void combinatorics(int m)
{
int i,j;
memset(a[0],0,MAX*sizeof(int));
a[0][MAX-1]=1;
for (i=1; i<=m/2; i++)
{
memcpy(a[i], a[i-1], MAX * sizeof(int));
multiply(a[i], MAX, m+1-i);
divide(a[i], MAX, i);
}
for(i=0;i<=m/2;i++)
{
for(j=0;j<MAX && a[i][j]==0;j++)
;
long long temp=a[i][j];
j++;
for(;j<MAX;j++)
temp=(temp*10000+a[i][j])%19871118;
mod[i]=temp;
}
for(i=0;i<=m/2;i++)
mod[m-i]=mod[i];//根据C(m,k)=C(m,m-k)
}
long long fun_mod(long long x,long long y)
{
long long b=1;
while(y)
{
if(y&1)
b=x*b%19871118;
x=x*x%19871118;
y>>=1;
}
return b;
}
int symbol(int xiaoye)
{
if(xiaoye%2==0)
return 1;
else
return -1;
}
long long ye(long long x,long long y,long long z)
{
long long result=((x*y)%19871118*z)%19871118;
return result;
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)==2)
{
if(m>n)//m>n直接输出0
{
cout<<0<<endl;
continue;
}
combinatorics(m);
long long result=0;
int i;
for(i=0;i<=m;i++)
result=(result+symbol(i)*ye(mod[i],m-i,fun_mod(m-i-1,n-1))+19871118)%19871118;
printf("%lld\n",result);
}
return 0;
}