【BZOJ4455】【ZJOI2016】小星星
这题考场上sb写了个状压DP+爆搜,出了考场才反应过来。回去把爆搜改成了状压DP,合并状态,在UOJ上能跑出9个点,BZOJ上刚好能卡过。
感觉这个做法比较简单就简单地讲一下好了。
从根节点开始做树形DP,记f[i][j][k]表示树上的编号为i的点,对应原图上编号为j的点,子树(包括这个点)上所有点对应原图上的点的集合为k的方案数,那么从子树到根的状态转移是很显然的。复杂度没仔细地计算过,大概地算了一下还是很理想的。
直接这么做可能会爆空间,而对于一棵子树,状态数最大是C(n,n/2)级别的,所以存储的时候另开几个数组记录一下状态就好了。
#include <ctime>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=17,maxsta=24311;
long long f[maxn][maxn][maxsta];
int vis[maxn][maxn][1<<maxn],sta[maxn][maxn][maxsta],num[maxn][maxn];
long long tempf[maxn][maxsta];
int tvis[maxn][1<<maxn],tsta[maxn][maxsta],tnum[maxn];
int map1[maxn][maxn],map2[maxn][maxn];
int n,m;
void Merge(int x,int xc,int y,int yc)
{
int xsta,ysta,nsta;
for (int i=1;i<=num[x][xc];i++)
for (int j=1;j<=num[y][yc];j++)
if (!((xsta=sta[x][xc][i])&(ysta=sta[y][yc][j])))
{
nsta=xsta|ysta;
if (!tvis[xc][nsta]) tvis[xc][nsta]=++tnum[xc],tsta[xc][tnum[xc]]=nsta;
tempf[xc][tvis[xc][nsta]]+=f[x][xc][i]*f[y][yc][j];
}
}
void DFS(int x,int fa)
{
for (int i=0;i<n;i++) if (i!=x && i!=fa && map2[x][i]) DFS(i,x);
for (int i=0;i<n;i++) vis[x][i][1<<i]=1,sta[x][i][1]=1<<i,f[x][i][1]=1,num[x][i]=1;
for (int j=0;j<n;j++) if (j!=x && j!=fa && map2[x][j])
{
memset(tempf,0,sizeof(tempf));
memset(tvis,0,sizeof(tvis));
memset(tnum,0,sizeof(tnum));
for (int i=0;i<n;i++) for (int k=0;k<n;k++) if (i!=k && map1[i][k]) Merge(x,i,j,k);
memcpy(f[x],tempf,sizeof(tempf));
memcpy(vis[x],tvis,sizeof(tvis));
memcpy(sta[x],tsta,sizeof(tsta));
memcpy(num[x],tnum,sizeof(tnum));
}
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1,x,y;i<=m;i++) scanf("%d%d",&x,&y),--x,--y,map1[x][y]=map1[y][x]=1;
for (int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),--x,--y,map2[x][y]=map2[y][x]=1;
DFS(0,-1);
long long res=0;
for (int i=0;i<n;i++) for (int j=1;j<=num[0][i];j++) res+=f[0][i][j];
printf("%lld\n",res);
return 0;
}
正解还在看...
评论 (0)