题意抽象出来就是 在 n*n 的 棋盘中 加了 m 个禁位, 放置 n 个棋子 ,每两个棋子不在同一行 同一列,问有多少中放置方式 ?
trick :
M 个禁位 中 有相同 坐标的点。
解法 : 利用 有禁位的排列的公式 (容斥原理):
n! - r1 *( n-1)! + r2*(n -2)! - r3*(n-3)! +..........
ri 指 在禁区中 选 i 个 位置的 方案数。
由于 m 的值较小 ,可直接 dfs暴力求方案数。
优化: 把独立的点(同行和同列 仅此一点 , 跟别的点不会产生冲突)统计出来。
把棋盘分割成两部分, 独立的点构成的点集 和 不独立的点构成的点集 。
不独立的爆搜 ,独立的点 组合数。
最终 根据棋盘多项式(母函数)的求法,把两部分组合起来。
加了优化之后 时间 0ms 。
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <stack>
#include <cstring>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <assert.h>
#include <queue>
#define REP(i,n) for(int i=0;i<n;i++)
#define TR(i,x) for(typeof(x.begin()) i=x.begin();i!=x.end();i++)
#define ALLL(x) x.begin(),x.end()
#define SORT(x) sort(ALLL(x))
#define CLEAR(x) memset(x,0,sizeof(x))
#define FILLL(x,c) memset(x,c,sizeof(x))
using namespace std;
const double EPS = 1e-8;
#define LL long long
#define pb push_back
const int mod = 55566677;
const int maxn = 51;
int s[maxn];
int n,m;
int x[50], y[50];
int x2[50],y2[50];
LL ans = 0;
int day[50],book[50];
int xnum[50];
int xnum2[50];
int xnum3[50];
int d[50];
int C[26][26];
void init(){
CLEAR(C);
C[0][0] =1;
for(int i = 1;i<=25;i++){
C[i][0] =1;
for(int j =1;j<=i;j++){
C[i][j] = C[i-1][j-1] + C[i-1][j];
C[i][j]%= mod;
}
}
}
int tot;
void dfs(int i,int num){
// cout << i << " "<< m <<endl;
if(i == m +1 ){
xnum[num]++;
return ;
}
if((!book[x[i]]) && (!day[y[i]])) {
book[x[i]]= day[y[i] ] = 1;
dfs(i+1,num+1);
book[x[i]] = day[y[i]] = 0;
}
dfs(i+1,num);
}
void solve(){
ans = 0;
CLEAR(book);
CLEAR(day);
CLEAR(xnum);
dfs(1,0);
CLEAR(xnum2);
for(int i=0;i<=tot;i++){
xnum2[i] = C[tot][i];
}
CLEAR(xnum3);
for(int i=0;i<=m;i++){
for(int j=0 ;j<=tot;j++){
xnum3[i+j] += xnum[i]*xnum2[j];
xnum3[i+j]%= mod;
}
}
// cout << m <<"m+tot "<<tot <<endl;
for(int i=0;i<=m+tot;i++){
// cout << xnum3[i]<< " "<<s[n-i]<<" "<<i<<" "<<n<<endl;
if(i&1){
ans -= xnum3[i] * (LL)s[n-i];
ans %= mod;
ans = ans + mod;
ans %= mod;
}else{
ans+= xnum3[i] * (LL)s[n-i];
ans %= mod;
}
}
printf("%d\n",(int)ans);
}
int main(){
init();
s[0]=1;
for(int i=1;i<=50;i++)
s[i]=((long long)s[i-1]*(long long)i)%mod;
while(~scanf("%d%d",&n,&m)){
int k = 0 ;
for(int i=1;i<=m;i++){
scanf("%d%d",&x2[i],&y2[i]);
for(int j = 1;j<i;j++){
if((x2[i] == x2[j]) && (y2[i] == y2[j])){
i -- ;
m -- ;
break;
}
}
}
// cout << m << endl;
// m = k;
for(int i=1;i<=m;i++){
d[i] =1;
for(int j =1;j<=m;j++){
if(i!= j){
if(x2[i] == x2[j]) d[i] = 0;
if(y2[i] == y2[j]) d[i] = 0;
}
}
}
int tmp = 0;
k = 0 ;
for(int i=1;i<=m;i++){
if(d[i]){
tmp ++ ;
}else{
k++;
x[k] = x2[i];
y[k] = y2[i];
}
}
m = k ;
tot = tmp;
solve();
}
return 0;
}