简单的组合计数问题
坐标为(i,j)的数通过旋转能得到 (j,n-i-1),(n-i-1z),(n-j-1),(n-j-1,i)
通过旋转再家翻转后(i,j)能得到 (i,j),(n-i-1,j),(i,n-j-1),(n-i-1,n-j-1),(j,i),(j,n-i-1),(n-j-1,i),(n-j-1,n-i-1)
通过翻转我们知道需要染色的地方只有矩阵的左上角(其他地方的颜色只能与这一块儿对应相等)
通过上面的关系,(i,j)一定要与(j,i)染色相同。这样的话总共需要染色数就为 size*(size+1)/2 其中size为左上角矩阵的长度 size=n/2(向上取整)
接下来要做的就是排除那些已经染过色的数了。 某一个点已经被染色一定对应着左上角上一个相应的点被染色了,只要找到这些点对应的左上角的坐标再标记成染过色就OK了。找到标记坐标的方法很简单(只要找 n-i-1和i中小于size的数 和 n-j-1和j中小于size的数就可以)
实现的时候需要用快速幂取模,如果所有的数都用long long则会超时(需要读入时开挂) 如果都用int会WA(做乘法时数据会溢出) 所以要么全部开int在快速幂那儿用long logn保存中间结果要么直接开long long 再加输入挂
#include <iostream>
#include <memory.h>
#include <cstdio>
using namespace std;
bool visit[5002][5002];
const int MOD = 100000007;
long long getRes(int k,int tot)
{
if(tot==0) return 1;
if(tot==1) return k;
long long tmp = getRes(k,tot/2);
if(tot%2==0) return tmp*tmp%MOD;
else return tmp*tmp%MOD*k%MOD;
}
int input()
{
int res=0;
char c=getchar();
while(c==' '||c=='\n') c=getchar();
while(true)
{
res+=c-'0';
c=getchar();
if(c<'0'||c>'9') break;
res*=10;
}
return res;
}
int main()
{
int n,m,k,a,b,tmp,size,tot;
while(scanf("%d%d%d",&n,&m,&k)!=EOF)
{
size = n/2;
if(n%2) size++;
memset(visit,0,sizeof(visit));
tot = 0;
while(m--)
{
a = input();
b = input();
if(a>=size) a=n-1-a;
if(b>=size) b=n-1-b;
if(!visit[a][b])
{
tot++;
visit[a][b]=visit[b][a]=true;
}
}
tot = size*(size+1)/2-tot;
printf("%I64d\n",getRes(k,tot));
}
return 0;
}