zoj 3687 The Review Plan I

本文探讨了一个特定的排列问题,在n*n的棋盘上放置n个棋子,避开m个禁位,确保任意两个棋子不在同一行或列。通过使用容斥原理和递归搜索算法,提出了一种高效的解决方案,并通过优化独立点处理,实现了0ms的运行时间。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


题意抽象出来就是 在  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;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值