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 }

 

 

posted @ 2013-02-17 17:32  proverbs  阅读(1322)  评论(0编辑  收藏  举报