bzoj1002 生成树计数 找规律

 

这道题第一眼是生成树计数,n是100,是可以用O(n^3)的求基尔霍夫矩阵的n-1阶的子矩阵的行列式求解的,但是题目中并没有说取模之类的话,就不好办了。

用高精度?有分数出现。

用辗转相除的思想,让它不出现分数。但过程中会出现负数,高精度处理负数太麻烦。

用Python打表?好吧,Python还不熟,写不出来。。。。。

所以,如果这道题我考场上遇到,最多用double骗到n<=20的情况的部分分。

 

最终只能求助于题解了。。。

好像是通过观察行列式的特点,推导出关于答案f(n)的递推式(f(n)=3*f(n-1)-f(n-2)+2)

这道题就这样水过了,收获是:

  1、题目可能属于某一类问题,该类问题又有通法可解,但题目一般不能直接套用之,此时就只能观察题目较之一般问题的特殊之处,尝试利用它帮助解题。

  2、当1行不通时,可以先用暴力把规模较小的解跑出来,再看看可否推出规律。

 

回顾一下高精:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #define M 10
 5 using namespace std;
 6 
 7 struct Num {
 8     int v[100], len;
 9     Num(){}
10     Num( int n ) {
11         memset( v, 0, sizeof(v) );
12         if( n==0 ) {
13             len = 1;
14             return;
15         }
16         len = 0;
17         while( n ) {
18             len++;
19             v[len] = n%M;
20             n /= M;
21         }
22     }
23     Num operator+( const Num &b ) const {
24         Num rt(0);
25         rt.len = max( len, b.len ) + 1;
26         for( int i=1; i<=rt.len; i++ ) {
27             rt.v[i] += v[i]+b.v[i];
28             rt.v[i+1] += rt.v[i]/M;
29             rt.v[i] %= M;
30         }
31         while( rt.len>1 && rt.v[rt.len]==0 ) rt.len--;
32         return rt;
33     }
34     Num operator-( const Num &b ) const {
35         Num rt(0);
36         rt.len = len;
37         for( int i=1; i<=rt.len; i++ ) {
38             rt.v[i] += v[i]-b.v[i];
39             if( rt.v[i]<0 ) {
40                 rt.v[i]+=M;
41                 rt.v[i+1]--;
42             }
43         }
44         while( rt.len>1 && rt.v[rt.len]==0 ) rt.len--;
45         return rt;
46     }
47     int count_bit( int b ) const {
48         int rt = 0;
49         while( b ) {
50             rt++;
51             b/=M;
52         }
53         return rt;
54     }
55     Num operator*( int b ) const {
56         Num rt(0);
57         rt.len = len+count_bit(b)+1;    //    b==3
58         for( int i=1; i<=rt.len; i++ ) {
59             rt.v[i] += v[i]*b;
60             rt.v[i+1] += rt.v[i]/M;
61             rt.v[i] %= M;
62         }
63         while( rt.len>1 && rt.v[rt.len]==0 ) rt.len--;
64         return rt;
65     }
66     void print() {
67         for( int i=len; i>=1; i-- )
68             printf( "%d", v[i] );
69         printf( "\n" );
70     }
71 };
72 
73 int n;
74 Num dp[110];
75 
76 int main() {
77     scanf( "%d", &n );
78     dp[1] = Num(1);
79     dp[2] = Num(5);
80     for( int i=3; i<=n; i++ )
81         dp[i] = dp[i-1]*3 +Num(2) - dp[i-2];
82     dp[n].print();
83 }
View Code

 

——————————————————————————————————————————

去补习了一下python,用python写了一个用辗转相除思想算行列式的算法(感觉python还是挺快的,还有高精度支持)

 1 #!/usr/bin/python
 2 
 3 from math import *
 4 
 5 
 6 def swap( a, b ):
 7     return b, a
 8 def abs( a ) :
 9     if a<0 :
10         return -a
11     else:
12         return a
13 def det( a, n ):
14     for i in range(0,n):
15         if a[i][i]==0:
16             return 0
17         for j in range(i+1,n):
18             while a[j][i]!=0 : 
19                 d = a[i][i]//a[j][i]
20                 for k in range(i,n) :
21                     a[i][k] = a[i][k]-a[j][k]*d
22                     a[j][k],a[i][k] = swap( a[j][k],a[i][k] )
23     ans = 1
24     for i in range(0,n):
25         ans = ans * a[i][i]
26     return abs(ans)
27 
28 def mod( a, m ):
29     return (a%m+m)%m
30 
31 def main():
32     for n in range( 1, 101 ):
33         if n==1 :
34             print 1
35             continue
36         if n==2 :
37             print 5
38             continue
39 
40         i = j = 0
41         a = [ [ 0 for j in range(n) ] for i in range(n) ]
42         i = 0
43         for i in range(0,n):
44             a[i][i] = 3
45             a[i][mod(i-1,n)] = -1
46             a[i][mod(i+1,n)] = -1
47         print det(a,n)
48 main()
View Code

将文件保存到bzoj1002.py,执行

  chmod +x hzoj1002.py

  ./hzoj1002.py

 

posted @ 2015-02-15 22:45  idy002  阅读(473)  评论(0编辑  收藏  举报