【NOI2007】 生成树计数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }