BZOJ 4487 [Jsoi2015] 染色问题

Description

棋盘是一个n×m的矩形,分成n行m列共n*m个小方格。现在萌萌和南南有C种不同颜色的颜料,他们希望把棋盘用这些颜料染色,并满足以下规定:
1.  棋盘的每一个小方格既可以染色(染成C种颜色中的一种) ,也可以不染色。
2.  棋盘的每一行至少有一个小方格被染色。
3.  棋盘的每一列至少有一个小方格被染色。
4.  种颜色都在棋盘上出现至少一次。
以下是一些将3×3棋盘染成C = 3种颜色(红、黄、蓝)的例子:

请你求出满足要求的不同的染色方案总数。只要存在一个位置的颜色不同,
即认为两个染色方案是不同的

Input

输入只有一行 3 个整数n,m,c。1 < = n,m,c < = 400

Output

输出一个整数,为不同染色方案总数。因为总数可能很大,只需输出总数
mod 1,000,000,007的值。

Sample Input

2 2 3

Sample Output

60

HINT

Source

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

数论+思路~

设有i行j列k颜色是可能存在的,那么此时的种类数为0<=i<=n,0<=j<=m,0<=k<=c,c[i][n]*c[j][m]*c[k][c]*(k+1)^(i*j)*(-1)^(n+m+c-i-j-K)。

这样O(nmc)的算法据说可以卡过……但是我被卡常了……

我们将有j的项提取出来,得到0<=i<=n,0<=j<=m,0<=k<=c,(-1)^(n+m+c-i-K) * c[i][n] * c[k][c] * ((k+1)^i)^j * c[j][m]*(-1)^(-j),

注意到后面包含j的项刚好是(1-k)^n的展开的形式。

所以原式可化简为0<=i<=n,0<=k<=c,(-1)^(n+m+c-i-K) * c[i][n] * c[k][c] *(1-(k+1)^i)^m。

另:取模这个东西真是莫名其妙……WA了半天结果是先乘了-1^k后取模……所以遇到这种情况要先取模再乘啊……


被卡常的代码:

#include
#define ll long long
#define modd 100000007

int n,m,cc,c[401][401],ans;

int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0' || ch>'9') {if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0' && ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	return x*f;
}

int mi(int u,int v)
{
	int now=1;u%=modd;
	while(v)
	{
		if(v&1) now=(ll)now*u%modd;
		u=(ll)u*u%modd;v>>=1;
	}
	return now;
}

int main()
{
	n=read();m=read();cc=read();c[0][0]=1;
	for(int i=1;i<=400;i++)
	{
		c[i][0]=1;
		for(int j=1;j<=400;j++) c[i][j]=(ll)(c[i-1][j]+c[i-1][j-1])%modd;
	}
	for(int i=0;i<=n;i++)
	  for(int j=0;j<=m;j++)
		for(int k=0;k<=cc;k++) ans=((ll)ans+(ll)mi(-1,n+m+cc-i-j-k)*c[n][i]*c[m][j]*c[cc][k]*mi(k+1,i*j))%modd;
	printf("%d\n",ans);
	return 0;
}


A了的代码:

#include
#define ll long long
#define modd 1000000007

int n,m,cc;
ll c[401][401],ans;

int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0' || ch>'9') {if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0' && ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	return x*f;
}

ll mi(ll u,ll v)
{
	ll now=1;u%=modd;
	while(v)
	{
		if(v&1) now=now*u%modd;
		u=u*u%modd;v>>=1;
	}
	return now;
}

ll kk(ll u)
{
	return u&1 ? modd-1:1;
}

int main()
{
	n=read();m=read();cc=read();c[0][0]=1;
	for(int i=1;i<=400;i++)
	{
		c[i][0]=1;
		for(int j=1;j<=400;j++) c[i][j]=(c[i-1][j]+c[i-1][j-1])%modd;
	}
	for(int i=0;i<=n;i++)
	  for(int k=0;k<=cc;k++)
	  {
		  ll now=(c[n][i]*c[cc][k]%modd)*kk(n^m^cc^i^k)%modd;
		  now=now*mi((1-mi(k+1,i)+modd)%modd,m)%modd;
		  ans=(ans+now)%modd;
	  }
	printf("%lld\n",ans);
	return 0;
}



你可能感兴趣的:(BZOJ,思路,数论)