BZOJ 1494 [NOI2007]生成树计数 矩阵乘法+DP
题解:
这种数据范围果断矩阵乘法,其实dp什么的也特别显然,就是求转移矩阵特别恶心。
用最小表示法(表示现学的压力比较大)表示连通性。
能多暴力就多暴力的枚举,反正数据范围小~
唯一需要注意的是转移的时候有两种情况是不合法的:
1、第(i-k)个点不和第(i-k+1)到i个点连通
2、第i个点连到已经在一个连通块中的两个点
wa了半天,然后不想做了的时候突然发现ac了。。莫名其妙。。。
代码比较丑陋。。
View Code
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 #include <cstring> 5 #include <algorithm> 6 7 #define N 10 8 #define SIZE 60 9 #define mod 65521 10 11 using namespace std; 12 13 struct MT 14 { 15 int x,y; 16 long long mt[SIZE][SIZE]; 17 }ans,zy; 18 19 int hash[100010],antihash[100010]; 20 int m,fa[N],col[N],cnt; 21 long long n; 22 bool map[N][N],vis[N][N]; 23 24 inline MT operator *(MT a,MT b) 25 { 26 MT c; memset(c.mt,0,sizeof c.mt); 27 c.x=a.x; c.y=b.y; 28 for(int i=1;i<=c.x;i++) 29 for(int j=1;j<=c.y;j++) 30 for(int k=1;k<=a.y;k++) 31 c.mt[i][j]=(c.mt[i][j]+a.mt[i][k]*b.mt[k][j])%mod; 32 return c; 33 } 34 35 inline int findfa(int x) 36 { 37 if(fa[x]!=x) return fa[x]=findfa(fa[x]); 38 return fa[x]; 39 } 40 41 inline void check() 42 { 43 memset(vis,1,sizeof vis); 44 for(int i=1;i<=m;i++) fa[i]=i; 45 for(int i=1;i<m;i++) 46 for(int j=1;j+i<=m;j++) 47 if(map[i][j]) 48 { 49 if(findfa(i)==findfa(i+j)) return;//判环 50 else fa[findfa(i)]=findfa(i+j); 51 } 52 for(int i=1;i<m;i++) 53 for(int j=1;j+i<=m;j++) 54 vis[i][i+j]=vis[i+j][i]=!map[i][j]; 55 for(int k=1;k<=m;k++)//传递闭包 56 for(int i=1;i<=m;i++) 57 for(int j=1;j<=m;j++) 58 { 59 if(vis[i][j]==0) continue; 60 if(vis[i][k]||vis[k][j]) continue; 61 vis[i][j]=0; 62 } 63 int tmp=0; 64 memset(col,-1,sizeof col); 65 for(int i=1;i<=m;i++)//最小表示法 66 { 67 for(int j=1;j<i;j++) 68 if(vis[j][i]==0) {col[i]=col[j];break;} 69 if(col[i]==-1) col[i]=tmp++; 70 } 71 tmp=0; 72 for(int i=1;i<=m;i++) tmp=tmp*10+col[i]; 73 if(hash[tmp]==0) 74 { 75 hash[tmp]=++cnt;//cnt个最小表示法 76 antihash[cnt]=tmp; 77 } 78 ans.mt[hash[tmp]][1]++;//最小表示法代表的方案数 79 } 80 81 inline void dfs(int x,int y) 82 { 83 if(x==m-1&&y==2) check(); 84 else if(x+y==m+1) dfs(x+1,1); 85 else 86 { 87 map[x][y]=1; dfs(x,y+1); 88 map[x][y]=0; dfs(x,y+1); 89 } 90 } 91 92 inline bool uni() 93 { 94 for(int i=2;i<=m;i++) 95 if(col[1]==col[i]) return 0; 96 return 1; 97 } 98 99 inline int trans(int zt) 100 { 101 memset(vis,1,sizeof vis); 102 for(int i=1;i<=m;i++) 103 for(int j=i+1;j<=m;j++) 104 if(col[i]==col[j]) vis[i][j]=vis[j][i]=0; 105 for(int i=1;i<=m;i++) 106 if(zt&(1<<(m-i))) vis[i][m+1]=vis[m+1][i]=0; 107 for(int k=1;k<=m+1;k++)//传递闭包 108 for(int i=1;i<=m+1;i++) 109 for(int j=1;j<=m+1;j++) 110 { 111 if(vis[i][j]==0) continue; 112 if(vis[i][k]||vis[k][j]) continue; 113 vis[i][j]=0; 114 } 115 memset(col,-1,sizeof col); 116 int tmp=0; 117 for(int i=2;i<=m+1;i++)//最小表示法 118 { 119 for(int j=2;j<i;j++) 120 if(vis[j][i]==0) {col[i]=col[j];break;} 121 if(col[i]==-1) col[i]=tmp++; 122 } 123 tmp=0; 124 for(int i=2;i<=m+1;i++) tmp=tmp*10+col[i]; 125 return tmp; 126 } 127 128 inline int solve(int c,int zt) 129 { 130 for(int i=m;i>=1;i--) 131 { 132 col[i]=c%10; 133 c/=10; 134 } 135 col[m+1]=-1; 136 for(int i=1;i<=m;i++) 137 for(int j=i+1;j<=m;j++) 138 if(col[i]==col[j]&&(zt&(1<<(m-i)))&&(zt&(1<<(m-j)))) return -1;//环 139 if(!(zt&(1<<(m-1)))&&uni()) return -1;//最左侧的点不连通 140 return trans(zt); 141 } 142 143 inline void get_det()//求出转移矩阵 144 { 145 ans.x=cnt; ans.y=1; 146 zy.x=zy.y=cnt; 147 for(int i=1;i<=cnt;i++) 148 { 149 int lim=1<<m,pa; 150 for(int j=0;j<lim;j++) 151 { 152 pa=solve(antihash[i],j); 153 if(pa==-1) continue; 154 zy.mt[hash[pa]][i]++; 155 } 156 } 157 } 158 159 inline void go() 160 { 161 scanf("%d%lld",&m,&n); 162 if(m==1) {puts("1");return;} 163 dfs(1,1); 164 get_det(); 165 n-=(long long)m; 166 while(n) 167 { 168 if(n&(1LL)) ans=zy*ans; 169 zy=zy*zy; 170 n>>=1LL; 171 } 172 printf("%lld\n",ans.mt[hash[0]][1]); 173 } 174 175 int main() 176 { 177 go(); 178 return 0; 179 }
没有人能阻止我前进的步伐,除了我自己!