题解:P8639 [蓝桥杯 2016 国 B] 生成树计数
首先观察数据范围,发现
在构建生成树的过程中,我们其实只关注每个点属于哪一个集合,更具体的,我们只关心每个点所在集合相互之间的关系,可以理解为联通块问题,直接用最小表示法维护轮廓线的状态。
具体如下图:
红色格子是维护的轮廓线,
绿色是我们目前考虑到的格子。
为了方便,我们称绿点上面点的状态为
下面开始分类讨论
绿色无点
此时上点和左点都无法连边,也就是状态无法延伸。
关键点:注意到上点此时无法延伸,若此时上点在轮廓线上没有其他点和它处在同一集合,也就是
所以要特判上点不联通的情况,否则将绿点置为
绿色有点
有点就要考虑和上点和左点的连边。
连上连左
此时要特判
否则,暴力地修改轮廓线上和
注意到状态更改时,可能就不符合最小表示法了,要暴力的推平重设状态。
上图状态就会变为
连上不连左
设
连左不连上
设
不连上也不连左
此时绿点一定在一个独立的集合,我们直接令
最后统计答案时只需要 dp 到最后一个点,保证最后轮廓线上要么
状态保存可以
推平重构就从前往后给没出现过的集合依次标号。
复杂度分析
可以爆搜得出
贴一下代码
#define N 1000010
#define max(x,y) (x>y?x:y)
#define sol(S,x) ((S>>(pre[x]))&7)
int ss[900],b;
int tot,id[N],vis[N];
int n,m,v[N][7],op,pre[7],st[2][900],top[2];
int dp[2][900];
void dfs(int x,int mx){
if(x==n+1){
++tot;ss[tot]=b;id[b]=tot;
vis[b]=b;
return;
}
for(int i=0;i<=mx+1;++i){
b|=(i<<pre[x]);dfs(x+1,max(mx,i));b^=(i<<pre[x]);
}
return;
}
void insert(int x,int y){
if(!dp[op][x]){st[op][++top[op]]=x;}
dp[op][x]=(dp[op][x]+y)%mod;
return;
}
int vs[8],edx,edy,stx,sty;
int solve(int S){
if(vis[S])return vis[S];
memset(vs,0,sizeof(vs));
int T=0,tot=0;
for(int i=1;i<=n;++i){
int y=sol(S,i);
if(y&&!vs[y])vs[y]=++tot;
T|=(vs[y]<<pre[i]);
}
vis[S]=T;
return T;
}
int check(int S){
for(int i(1);i<=n;++i)if(sol(S,i)>1)return 0;
return 1;
}
signed main(){
read(n),read(m);
pre[0]=100;
for(int i=1;i<=n;++i)pre[i]=(i-1)*3;
dfs(1,0);
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
char c;cin>>c;
if(c=='E')v[j][i]=1;
}
}
for(int i(1);i<=m;++i){
for(int j(1);j<=n;++j){
if(v[i][j])edx=i,edy=j;
}
}
insert(id[0],1);
int ansl=0;
for(int i=1;i<=m;++i){
for(int j=1;j<=n;++j){
op^=1;top[op]=0;
int opp=op^1;
for(int k=1;k<=top[opp];++k){
int S=ss[st[opp][k]],z=dp[opp][st[opp][k]];dp[opp][st[opp][k]]=0;
int y=sol(S,j),yy=sol(S,j-1),opl=0;
if(!y)opl=1;
else {
for(int k(1);k<=n;++k){
if(j!=k&&y==sol(S,k)){opl=1;break;}
}
}
if(!v[i][j]){if(opl)insert(id[solve(S^(y<<pre[j]))],z);}
else{
if(y&&yy&&y!=yy){
int T=S;
for(int k=1;k<=n;++k){
if(yy==sol(S,k)){
T^=(yy<<pre[k]);
T|=(y<<pre[k]);
}
}
insert(id[solve(T)],z);
}
if(y)insert(id[S],z);
if(yy&&opl==1)insert(id[solve(((S^(y<<pre[j]))|(yy<<pre[j])))],z);
if(opl==1)insert(id[solve(((S^(y<<pre[j]))|(7<<pre[j])))],z);
}
}
if(i==edx&&j==edy){ansl=1;break;}
}
if(ansl)break;
}
int ans=0;
for(int i=1;i<=top[op];++i){
if(check(ss[st[op][i]])){
ans=(ans+dp[op][st[op][i]])%mod;
}
}
writeln(ans);
return 0;
}