1026 [HAOI2016]放棋子 组合数学 错位问题
链接:https://ac.nowcoder.com/acm/contest/26656/1026
来源:牛客网
题目描述
给你一个N*N的矩阵,每行有一个障碍,数据保证任意两个障碍不在同一行,任意两个障碍不在同一列,要求你在这个矩阵上放N枚棋子(障碍的位置不能放棋子),要求你放N个棋子也满足每行只有一枚棋子,每列只有一枚棋子的限制,求有多少种方案。
输入描述:
第一行一个N,接下来一个N*N的矩阵。N ≤ 200,0表示没有障碍,1表示有障碍,输入格式参考样例
输出描述:
一个整数,即合法的方案数。
分析
首先由于n <= 200,所以必须用高精度(太长了)
错位问题。对于第n个位置考虑两种情况:
1.前n个位置已经错位,随便拿一个和最后一个交换即可
2.前n个有一个在自己的位置上,拿最后一个和这个交换即可
递推公式:f[n] = (n - 1) * ( f[n-1] + f[n-2]);
#include <bits/stdc++.h> using namespace std; typedef long long ll; #define pii pair<int,int> const int mod=1e9+7; const int INF=0x3f3f3f3f; const int N=2e2+5; const int ten[4]={1,10,100,1000}; const int max1=1000; struct BigNumber{ int d[max1]; BigNumber (string s){ int len=s.size(); d[0]=(len-1)/4+1; for (int i=1;i<max1;i++) d[i]=0; for (int i=len-1;i>=0;i--){ int j=(len-i-1)/4+1; int k=(len-i-1)%4; d[j]+=ten[k]*(s[i]-'0'); } while (d[0]>1&&d[d[0]]==0) d[0]--; } BigNumber(){ *this=BigNumber(string("0")); } string toString(){ string s(""); int pos=0; for (int i=3;i>=1;i--) if (d[d[0]]>=ten[i]) { pos=i; break; } int temp=d[d[0]]; for (int j=pos;j>=0;j--){ s=s+(char)(temp/ten[j]+'0'); temp%=ten[j]; } for (int i=d[0]-1;i>0;i--){ temp=d[i]; for (int j=3;j>=0;j--){ s=s+(char)(temp/ten[j]+'0'); temp%=ten[j]; } } return s; } } zero("0"),one("1"),d,temp,mid1[15]; bool operator < (const BigNumber &a,const BigNumber &b){ if (a.d[0]!=b.d[0])return a.d[0]<b.d[0]; for (int i=a.d[0];i>=0;i--)if (a.d[i]!=b.d[i]) return a.d[i]<b.d[i]; return false; } BigNumber operator + (const BigNumber &a, const BigNumber &b){ BigNumber c; c.d[0]=max(a.d[0],b.d[0]); int x=0; for (int i=1;i<=c.d[0];i++){ x=a.d[i]+b.d[i]+x;; c.d[i]=x%10000; x/=10000; } while (x!=0){ c.d[++c.d[0]]=x%10000; x/=10000; } return c; } BigNumber operator - (const BigNumber &a, const BigNumber &b){ BigNumber c; c.d[0]=a.d[0]; int x=0; for (int i=1;i<=c.d[0];i++){ x=10000+a.d[i]-b.d[i]+x; c.d[i]=x%10000; x=x/10000-1; } while ((c.d[0]>1)&&(c.d[c.d[0]]==0)) c.d[0]--; return c; } BigNumber operator * (const BigNumber &a, const BigNumber &b){ BigNumber c; c.d[0]=a.d[0]+b.d[0]; int x; for (int i=1;i<=a.d[0];i++){ x=0; for (int j=1;j<=b.d[0];j++){ x=a.d[i]*b.d[j]+x+c.d[i+j-1]; c.d[i+j-1]=x%10000; x/=10000; } c.d[i+b.d[0]]=x; } while ((c.d[0]>1)&&(c.d[c.d[0]]==0)) c.d[0]--; return c; } bool smaller(const BigNumber &a, const BigNumber &b, int delta){ if (a.d[0]+delta!=b.d[0]) return a.d[0]+delta<b.d[0]; for (int i=a.d[0];i>0;i--) if (a.d[i]!=b.d[i+delta]) return a.d[i]<b.d[i+delta]; return true; } void Minus (BigNumber &a, BigNumber &b,int delta){ int x=0; for (int i=1;i<=a.d[0]-delta;i++){ x=10000+a.d[i+delta]-b.d[i]+x; a.d[i+delta]=x%10000; x=x/10000-1; } while (a.d[0]>1&&a.d[a.d[0]]==0) a.d[0]--; } BigNumber operator * (const BigNumber &a, const int &k){ BigNumber c; c.d[0]=a.d[0]; int x=0; for (int i=1;i<=a.d[0];i++){ x=a.d[i]*k+x; c.d[i]=x%10000; x/=10000; } while (x>0){ c.d[++c.d[0]]=x%10000; x/=10000; } while ((c.d[0]>1)&&(c.d[c.d[0]]==0)) c.d[0]--; return c; } BigNumber operator / (const BigNumber &a, const BigNumber &b){ BigNumber c; d=a; mid1[0]=b; for (int i=1;i<=13;i++){ mid1[i]=mid1[i-1]*2; } for (int i=a.d[0]-b.d[0];i>=0;i--){ int temp=8192; for (int j=13;j>=0;j--){ if (smaller(mid1[j],d,i)){ Minus(d,mid1[j],i); c.d[i+1]+=temp; } temp/=2; } } c.d[0]=max(1,a.d[0]-b.d[0]+1); while ((c.d[0]>1)&&(c.d[c.d[0]]==0)) c.d[0]--; return c; } bool operator == (const BigNumber &a, const BigNumber &b){ if (a.d[0]!=b.d[0]) return false; for (int i=1;i<=a.d[0];i++) if (a.d[i]!=b.d[i])return false; return true; } BigNumber fac[N]; BigNumber Comb(int x,int y){ if (x<y) return zero; return fac[x]/fac[y]/fac[x-y]; } int main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); int n; cin>>n; BigNumber ans=zero; fac[0]=one; for (int i=1;i<=n;i++) fac[i]=fac[i-1]*i; for (int i=0;i<=n;i++){ if (i&1) ans=ans-fac[n-i]*Comb(n,i); else ans=ans+fac[n-i]*Comb(n,i); } cout<<ans.toString(); return 0; }