洛谷 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]放棋子 题解的主要内容,如果未能解决你的问题,请参考以下文章

P3182 [HAOI2016]放棋子

P3182 [HAOI2016]放棋子

JZYZOJ1544 [haoi2016T2]放棋子 错排公式 组合数学 高精度

LibreOJ #2061. 「HAOI2016」放棋子

[BZOJ4563][Haoi2016]放棋子

[HAOI2016]放棋子