【BZOJ2576】[JSOI2011]序的計數 (動態規劃)
阿新 • • 發佈:2019-02-14
urn getchar 目標 for getch 能夠 code als 動態規劃
轉移的時候枚舉一個\(u\)的相鄰點,記憶化搜索即可。
【BZOJ2576】[JSOI2011]序的計數 (動態規劃)
題面
BZOJ
題解
首先構建一個新的虛擬節點連接所有目標節點,強行將其作為第一個被訪問的節點,這樣子就解決了圖不連通的問題。
除了目標節點外,所有其他點都可以縮成一個節點。
這樣子的圖實際上只有\(k+2\)個節點,\(k+1\)個目標節點。
預處理\(G[S][u]\)表示已經在\(dfs\)序中出現過的點的集合為\(S\),當前在點\(u\)能夠訪問到的點。
設\(f[S][u]\)表示當前在點\(u\),已經確定\(dfs\)序的集合為\(S\)的\(dfs\)序的方案數。
註意如果一個點和不合法的點有連邊,那麽這個點不能回朔。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; #define ll long long #define MAX 120 inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } int n,m,K,id[MAX],S[20]; bool g[MAX][MAX],M[20][20]; ll f[1<<19][20]; int G[1<<19][20]; int dfs(int u,int S) { if(~G[S][u])return G[S][u]; int ret=1<<u; for(int i=0;i<K;++i) if(M[u][i]&&!(S&(1<<i)))ret|=dfs(i,S|(1<<i)); return G[S][u]=ret; } ll Solve(int u,int S) { if(~f[S][u])return f[S][u]; if(G[S][u]==1<<u)return S==(1<<K)-1||!M[u][K]; ll ret=0; for(int i=0;i<K;++i) if(M[u][i]&&!(S&(1<<i))) ret+=Solve(i,S|(1<<i))*Solve(u,S|G[S|(1<<i)][i]); return f[S][u]=ret; } int main() { n=read();m=read();K=read();K+=1; for(int i=1,u,v;i<=m;++i)u=read(),v=read(),g[u][v]=g[v][u]=1; for(int i=1;i<=n;++i)id[i]=K; for(int i=0;i<K-1;++i)S[i]=read(),id[S[i]]=i,M[i][K-1]=M[K-1][i]=1; for(int i=0;i<=K;++i) for(int j=1;j<=n;++j)M[i][id[j]]|=g[S[i]][j]; memset(G,-1,sizeof(G));memset(f,-1,sizeof(f)); for(int i=0;i<(1<<K);++i) for(int j=0;j<K;++j) if(i&(1<<j))dfs(j,i); printf("%lld\n",Solve(K-1,1<<(K-1))); return 0; }
【BZOJ2576】[JSOI2011]序的計數 (動態規劃)