洛谷 P3182 [HAOI2016]放棋子 题解
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洛谷 P3182 [HAOI2016]放棋子 题解相关的知识,希望对你有一定的参考价值。
此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置。
题目链接:https://www.luogu.org/problem/show?pid=3182
题目描述
给你一个N*N的矩阵,每行有一个障碍,数据保证任意两个障碍不在同一行,任意两个障碍不在同一列,要求你在这个矩阵上放N枚棋子(障碍的位置不能放棋子),要求你放N个棋子也满足每行只有一枚棋子,每列只有一枚棋子的限制,求有多少种方案。
输入输出格式
输入格式:第一行一个N,接下来一个N*N的矩阵。N<=200,0表示没有障碍,1表示有障碍,输入格式参考样例
输出格式:一个整数,即合法的方案数。
输入输出样例
输入样例#1:
2 0 1 1 0
输出样例#1:
1
分析:
最近模拟考到一个类似的题,就详细写一下思考过程。
开始的时候没啥思路,以为可能是DP之类的,照着20分写了个搜索。
然后考虑优化搜索,发现答案只和n有关系,矩阵的行随机交换之后答案不变。
这时候就猜到这题是有规律的,于是输出n<=10时的答案。输出如下:
0,1,2,9,44,265,1854,14833,133496,1334961
别的数可能看不出什么规律,44是最明显的,只能拆分为4*11,而11 = 2+9
推导出计算公式:ans[n] = (ans[n-1]+ans[n-2])*(n-1)(后来发现这是错排公式)
n的范围是<=2000,会爆long long,所以还得写个高精。
我写高精一般都非常丑陋...看看思路就好qwq
AC代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 6 inline void read(int &x) 7 { 8 x = 0;char ch = getchar(), c = ch; 9 while(ch < ‘0‘ || ch > ‘9‘)c = ch, ch = getchar(); 10 while(ch <= ‘9‘ && ch >= ‘0‘)x = x * 10 + ch - ‘0‘, ch = getchar(); 11 if(c == ‘-‘)x = -x; 12 } 13 14 inline int Max(int a,int b) 15 {return a>b?a:b;} 16 17 inline int Min(int a,int b) 18 {return a<b?a:b;} 19 20 int n,mp; 21 long long pre[11] = {0,0,1,2,9,44,265,1854,14833,133496,1334961}; 22 //ans[n] = (ans[n-1]+ans[n-2])*(n-1) 23 24 struct bign 25 { 26 int len; 27 int num[10005]; 28 29 bign(){ 30 len = 0;memset(num,0,sizeof(num)); 31 } 32 33 void clear(){ 34 len = 0;memset(num,0,sizeof(num)); 35 } 36 }ans,tmp,tmp1,tmp2; 37 38 bign operator + (bign a,bign b) 39 { 40 ans.clear(); 41 int len = Max(a.len,b.len)+1; 42 int x = 0; 43 for(int i = 1;i <= len;++ i) 44 { 45 ans.num[i] = a.num[i]+b.num[i]+x; 46 x = ans.num[i]/10; 47 ans.num[i] %= 10; 48 } 49 if(!ans.num[len]) len --; 50 ans.len = len; 51 return ans; 52 } 53 54 bign operator * (bign a,int b) 55 { 56 ans.clear(); 57 int len = a.len+5; 58 int x = 0; 59 for(int i = 1;i <= len;++ i) 60 { 61 ans.num[i] = a.num[i]*b + x; 62 x = ans.num[i]/10; 63 ans.num[i] %= 10; 64 } 65 while(!ans.num[len]) len --; 66 ans.len = len; 67 return ans; 68 } 69 70 inline bign change(long long num) 71 { 72 ans.clear(); 73 register int cnt = 0; 74 while(num) 75 { 76 ans.num[++cnt] = num%10; 77 num /= 10; 78 } 79 ans.len = cnt; 80 return ans; 81 } 82 83 inline void put(bign a) 84 { 85 for(register int i = a.len;i >= 1;-- i) 86 printf("%d",a.num[i]); 87 printf("\n"); 88 } 89 90 int main() 91 { 92 // freopen("firstmeet.in","r",stdin); 93 // freopen("firstmeet.out","w",stdout); 94 read(n); 95 for(register int i = 1;i <= n;++ i) 96 for(register int j = 1;j <= n;++ j) 97 read(mp); 98 if(n <= 10){ 99 printf("%lld\n",pre[n]); 100 return 0; 101 } 102 else 103 { 104 tmp1 = change(pre[9]),tmp2 = change(pre[10]); 105 for(register int i = 11;i <= n;++ i) 106 { 107 tmp = tmp2; 108 tmp2 = (tmp1+tmp2)*(i-1); 109 tmp1 = tmp; 110 } 111 put(tmp2); 112 } 113 return 0; 114 }
以上是关于洛谷 P3182 [HAOI2016]放棋子 题解的主要内容,如果未能解决你的问题,请参考以下文章