【NOI2007】 生成树计数

 

题解

 

解法1:行列式
 1 (*
 2     Problem:    NOI2007 生成树计数
 3     Author :    Chen Yang
 4     Time   :    2012.5.30 8:00 am
 5     State  :    40分
 6     Memo   :    行列式求生成树个数
 7 *)
 8 program count;
 9 uses math;
10 const std=65521;
11 var
12   n,k,ans:int64;
13   i,j:longint;
14   a:array[0..3000,0..3000] of longint;
15   b:array[0..3000] of longint;
16   get:array[0..3000] of boolean;
17 //=========================
18 procedure work;
19 var
20   i,j:longint;
21   t,s,tmp:int64;
22 begin
23   t:=0;
24   //for i:=1 to n do write(b[i],' '); writeln;
25   for i:=1 to n do
26   for j:=i+1 to n do
27   if b[i]>b[j] then inc(t);
28   if t and 1=0 then s:=1 else s:=-1;
29   tmp:=1;
30   for i:=1 to n do
31   tmp:=tmp*int64(a[i,b[i]]);
32   ans:=ans+tmp*s;
33   if ans>std then ans:=ans mod std;
34 end;
35 //=========================
36 procedure find(x:longint);
37 var
38   i:longint;
39 begin
40   if x=n+1 then
41   begin
42     work;
43     exit;
44   end;
45   for i:=1 to n do
46   if (not get[i])and(a[x,i]<>0) then
47   begin
48     get[i]:=true; b[x]:=i;
49     find(x+1);
50     get[i]:=false; b[x]:=0;
51   end;
52 end;
53 //=========================
54 begin
55   assign(input,'count.in'); reset(input);
56   assign(output,'count.out'); rewrite(output);
57   read(k,n);
58   for i:=1 to n do
59   for j:=1 to n do
60   begin
61     if i=j then
62     a[i,j]:=i-max(1,i-k)+min(n,k+i)-i else
63     if abs(i-j)<=k then a[i,j]:=-1 else
64     a[i,j]:=0;
65   end;
66   dec(n);
67   {n:=3;
68   a[1,1]:=1; a[1,2]:=2; a[1,3]:=3;
69   a[2,1]:=4; a[2,2]:=5; a[2,3]:=6;
70   a[3,1]:=7; a[3,2]:=8; a[3,3]:=0;}
71   find(1);
72   while ans<0 do inc(ans,std);
73   writeln(ans);
74   close(input); close(output);
75 end.

 

解法2:状压DP(朴素)
  1 /*
  2     Problem:    NOI2007 生成树计数
  3     Author:        Chen Yang
  4     Time:        2012.5.30 8:12 pm
  5     State:        80分
  6     Memo:        状压DP(朴素)
  7 */
  8 #include <cstdio>
  9 #include <cstdlib>
 10 #include <cstring>
 11 #include <string>
 12 #include <algorithm>
 13 typedef long long big;
 14 using namespace std;
 15 const int mod=65521;
 16 const int NUM[6] = {1,1,1,3,16,125};
 17 big n, k;
 18 int sum, a[10], b[10], s[100], father[10];
 19 int g[100][100], num[100], id[100000];
 20 big f[10010][100];
 21 bool get[10];
 22 
 23 bool check()
 24 {
 25     memset(get, 0, sizeof get);
 26     for (int i=1; i<=k; ++i) get[a[i]]=1;
 27     for (int i=1; i<=k; ++i) get[a[i]]=1;
 28     for (int i=k, p=0; i; --i)
 29     if (get[i]) p = 1; 
 30     else if (p) return 0;
 31     for (int i=1; i<=k; ++i)
 32     {    
 33         int p = 0;
 34         for (int j=1; j<=i; ++j)
 35         if (a[i]==a[j]) p = 1;
 36         else if (a[i]<a[j] && !p) return 0;
 37     }
 38     return 1;
 39 }
 40 
 41 void find(int x)
 42 {
 43     if (x==1) 
 44     {
 45         a[1] = 1;
 46         if (check())  
 47         {
 48             s[++sum] = 1;
 49             for (int i=2; i<=k; ++i) 
 50             s[sum] = s[sum]*10+a[i];
 51             id[s[sum]] = sum;
 52         }
 53         return;
 54     }
 55     a[x] = x;
 56     for (; a[x]; --a[x]) find(x-1);
 57 }
 58 
 59 int getroot(int x) 
 60 {
 61     if (x==father[x]) return x;
 62     father[x] = getroot(father[x]);
 63     return father[x];
 64 }
 65 
 66 inline void merge(int x, int y)
 67 {
 68     int i = getroot(x), j = getroot(y);
 69     if (i==j) return;
 70     father[i] = j;
 71 }
 72 
 73 void dfs(int x, int y, int z)
 74 {
 75     if (x==0)
 76     {
 77         for (int i=1; i<=k; ++i) if (get[i])
 78         for (int j=1; j<=k; ++j)
 79             if (get[j] && i!=j && a[i]==a[j]) return;
 80         memset(b, 0, sizeof b);
 81         for (int i=1; i<k+2; ++i) father[i] = i;
 82         for (int i=1; i<k+1; ++i)
 83         {
 84             if (get[i]) merge(i+1,1);
 85             for (int j=1; j<k+1; ++j)
 86             if     (a[i]==a[j]) merge(i+1,j+1);
 87         }
 88         int p = 0;
 89         for (int i=k; i; --i)
 90         {
 91             b[i] = b[getroot(i)]? b[father[i]]:++p;
 92             b[father[i]] = b[i];
 93         }
 94         int S = 0;
 95         for (int i=k; i; --i) S = S*10+b[i];
 96         ++g[z][id[S]];
 97         return;
 98     }
 99     get[x] = 1; dfs(x-1, y, z);
100     if (!y && x==k) return;
101     get[x] = 0; dfs(x-1, y, z);
102 }
103 
104 void prepare()
105 {
106     find(k);
107     memset(g, 0,sizeof(g));
108     for (int i=1; i<=sum; ++i)
109     {
110         memset(get, 0, sizeof(get));
111         int t = s[i], p = 0;
112         while (t) { a[++p] = t%10; t /= 10; }
113         p = 0;
114         for (int j=1; j<k; ++j) if (a[j]==a[k]) p = 1; 
115         for (int j=1; j<=k; ++j)
116         {
117             int p = 1;
118             for (int i1=1; i1<=k; ++i1) 
119                 if (j!=i1 && a[j]==a[i1]) ++p;
120             if (num[i]<p) num[i] = p;
121         }
122         dfs(k, p, i);
123     }
124 }
125 
126 void work()
127 {
128     for (int i=1; i<=sum; ++i) f[k][i] = NUM[num[i]];
129     for (int i=k+1; i<=n; ++i)
130     for (int j=1; j<=sum; ++j)
131     for (int i1=1; i1<=sum; ++i1)
132     if (g[i1][j]) (f[i][j] += f[i-1][i1]*g[i1][j])%=mod;
133     printf("%lld\n", f[n][sum]);
134 }
135 
136 int main()
137 {
138     freopen("count.in", "r", stdin);
139     freopen("count.out", "w", stdout);
140     scanf("%lld%lld", &k, &n);
141     prepare();
142     work();
143     return 0;
144 }

 

解法3:状态压缩DP(矩阵乘法优化)
  1 /*
  2     Problem:    NOI2007 生成树计数
  3     Author:        Chen Yang
  4     Time:        2012.5.30 9:25 pm
  5     State:        Solved
  6     Memo:        状态压缩DP,矩阵乘法优化
  7 */
  8 #include <cstdio>
  9 #include <cstdlib>
 10 #include <cstring>
 11 #include <string>
 12 #include <algorithm>
 13 typedef long long big;
 14 using namespace std;
 15 const int mod=65521;
 16 const int NUM[6] = {0,1,1,3,16,125};
 17 big n, k;
 18 big f[100];
 19 bool get[10];
 20 int sum, a[10], b[10], s[100], father[10];
 21 int g[100][100], num[100], id[100000];
 22 
 23 struct mat
 24 {
 25     big t[100][100];
 26     mat() {memset(t, 0, sizeof t);}
 27     mat &operator *=(const mat &b)
 28     {
 29         mat c;
 30         for (int i=1; i<sum+1; ++i)
 31         for (int j=1; j<sum+1; ++j)
 32         for (int i1=1; i1<sum+1; ++i1)
 33         (c.t[i][j] += t[i][i1]*b.t[i1][j]) %= mod;
 34         memcpy(t, c.t, sizeof c.t);
 35         return *this;
 36     }
 37 } c, tmp;
 38 
 39 bool check()
 40 {
 41     memset(get, 0, sizeof get);
 42     for (int i=1; i<=k; ++i) get[a[i]]=1;
 43     for (int i=k, p=0; i; --i) 
 44     if (get[i]) p = 1; 
 45     else if (p) return 0;
 46     for (int i=1; i<=k; ++i)
 47     {    
 48         int p = 0;
 49         for (int j=1; j<=i; ++j)
 50         if (a[i]==a[j]) p = 1;
 51         else if (a[i]<a[j] && !p) return 0;
 52     }
 53     return 1;
 54 }
 55 
 56 void find(int x)
 57 {
 58     if (x==1) 
 59     {
 60         a[1] = 1;
 61         if (check())  
 62         {
 63             s[++sum] = 1;
 64             for (int i=2; i<=k; ++i) 
 65             s[sum] = s[sum]*10+a[i];
 66             id[s[sum]] = sum;
 67         }
 68         return;
 69     }
 70     a[x] = x;
 71     for (; a[x]; --a[x]) find(x-1);
 72 }
 73 
 74 int getroot(int x) 
 75 {
 76     if (x==father[x]) return x;
 77     father[x] = getroot(father[x]);
 78     return father[x];
 79 }
 80 
 81 inline void merge(int x, int y)
 82 {
 83     int i = getroot(x), j = getroot(y);
 84     if (i==j) return;
 85     father[i] = j;
 86 }
 87 
 88 void dfs(int x, int y, int z)
 89 {
 90     if (x==0)
 91     {
 92         for (int i=1; i<=k; ++i) if (get[i])
 93         for (int j=1; j<=k; ++j)
 94         if (get[j] && i!=j && a[i]==a[j]) return;
 95         memset(b, 0, sizeof b);
 96         for (int i=1; i<k+2; ++i) father[i] = i;
 97         for (int i=1; i<k+1; ++i)
 98         {
 99             if (get[i]) merge(i+1,1);
100             for (int j=1; j<k+1; ++j)
101             if (a[i]==a[j]) merge(i+1,j+1);
102         }
103         int p = 0;
104         for (int i=k; i; --i)
105         {
106             b[i] = b[getroot(i)]? b[father[i]]:++p;
107             b[father[i]] = b[i];
108         }
109         int S = 0;
110         for (int i=k; i; --i) S = S*10+b[i];
111         ++g[z][id[S]];
112         return;
113     }
114     get[x] = 1; dfs(x-1, y, z);
115     if (!y && x==k) return;
116     get[x] = 0; dfs(x-1, y, z);
117 }
118 
119 void prepare()
120 {
121     find(k);
122     memset(g, 0,sizeof(g));
123     for (int i=1; i<=sum; ++i)
124     {
125         memset(get, 0, sizeof(get));
126         int t = s[i], p = 0;
127         while (t) { a[++p] = t%10; t /= 10; }
128         for (int j=1; j<=k; ++j)
129         {
130             int p = 1;
131             for (int i1=1; i1<=k; ++i1)
132                 if (j!=i1 && a[j]==a[i1]) ++p;
133             if (num[i]<p) num[i] = p;
134         }
135         p = 0;
136         for (int j=1; j<k; ++j) if (a[j]==a[k]) p = 1; 
137         dfs(k, p, i);
138     }
139 }
140 
141 void quick(big b)
142 {
143     for (int i=1; i<100; ++i) tmp.t[i][i] = 1;
144     for (; b; b >>= 1, c *= c) if (b&1) tmp *= c;
145 }
146 
147 void work()
148 {
149     for (int i=1; i<=sum; ++i) f[i] = NUM[num[i]];
150     for (int i=1; i<=sum; ++i)
151     for (int j=1; j<=sum; ++j)  c.t[i][j] = g[j][i];
152     quick(n-k);
153     big ans = 0;
154     for (int i=1; i<sum+1; ++i) (ans += tmp.t[sum][i]*f[i]) %= mod;
155     printf("%lld\n", ans);
156 }
157 
158 int main()
159 {
160     freopen("count.in", "r", stdin);
161     freopen("count.out", "w", stdout);
162     scanf("%lld%lld", &k, &n);
163     prepare();
164     work();
165     return 0;
166 }

 

posted @ 2012-05-31 08:40  datam  阅读(746)  评论(0编辑  收藏  举报