XLIV.CF599E Sandy and Nuts
神题。
本题给我一个忠告:无论什么题,都要先看数据范围(废话)。
没看到\(n\leq 13\)之前以为是道毒瘤题,看到之后……还是毒瘤题。
因为数据范围小,可以状压。
先不考虑LCA和边的限制。设\(f[x][U]\)表示:在以\(x\)为根的子树中,选择了\(U\)里面的点,的方案数。
转移就是枚举\(u\subseteq \{U\setminus x\}\),其中\(\setminus\)符号表示从某个集合中删掉一个数/一个集合。这个\(u\)表示\(x\)的某个儿子所包含的子树集。然后就有
\(f[x][U]=\sum\limits_{u\subseteq \{U\setminus x\}}\sum\limits_{y\in u}f[y][u]*f[x][{U\setminus u}]\)
但是这个枚举会重复计算:假设\(x\)有两个儿子\(y_1\)和\(y_2\),那么枚举\(y_1\)时,\(y_2\)的情况会被算上;同时,枚举\(y_2\)时,\(y_1\)也会被计算!
这样,我们必须只计算包含某个点\(pos\)的那种方案所贡献的答案。即,随便找出一个\(pos\in\{U\setminus x\}\),则只有\(u\ni pos\)的\(u\)才是合法的\(u\)。
下面我们考虑加上边和\(LCA\)的限制,什么样的\(u\)才是合法的\(u\)。
I.LCA的限制
I.I.确保LCA是LCA而不是单纯的CA(common ancestor)。
即,对于\((a_i,b_i,c_i)\),如果有\(c_i=x\),则\(a_i\in u\)与\(b_i \in u\)不能同时出现,否则它们的LCA就不是\(x\)了。
I.II.确保LCA一定是A(ancestor)
即,对于\((a_i,b_i,c_i)\),如果有\(c_i\in u\),必有\(a_i\in u\)且\(b_i \in u\)。
II.边的限制
II.I.确保边的存在
即,对于\((a_i,b_i)\),如果有\(a_i\neq x\)且\(b_i\neq x\)但是\(a_i\in u\)与\(b_i\in u\)却有且只有一个条件成立,则这条边不可能存在。
II.II.确保边的可能
即,对于所有的\(y\ \operatorname{s.t.}\ \exists(x,y)\),一个\(u\)里最多只能有这么一个\(y\),因为一棵子树中最多只能同父亲连一条边。
如果只存在一个\(y\in u\),那么转移就只能从这个\(y\)而来,即
\(f[x][U]=f[y][u]*f[x][{U\setminus u}]\)
否则,即不存在\(y\in u\),就是上面的式子
\(f[x][U]=\sum\limits_{y\in u}f[y][u]*f[x][{U\setminus u}]\)
边界为\(f[x][\{x\}]=1\),最终答案为\(f[0][\{0\sim n-1\}]\)。
为了方便,采取记忆化搜索的形式实行。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,p,f[15][1<<15];
bool g[15][15];
pair<int,int>e[15];
pair<pair<int,int>,int>l[110];
bool in(int x,int y){return x&(1<<y);}
int dfs(int x,int U){
int &res=f[x][U],pos=0;
// printf("%d,%d:%d\n",x,U,res);
if(~res)return f[x][U];
U^=(1<<x),res=0;
for(;pos<n;pos++)if(in(U,pos))break;
for(int u=U;u;u=(u-1)&U){
if(!in(u,pos))continue;
bool ok=true;
for(int i=0;i<p;i++)if(l[i].second==x&&in(u,l[i].first.first)&&in(u,l[i].first.second)){ok=false;break;}
if(!ok)continue;
for(int i=0;i<p;i++)if(in(u,l[i].second)&&(!in(u,l[i].first.first)||!in(u,l[i].first.second))){ok=false;break;}
if(!ok)continue;
for(int i=0;i<m;i++)if(e[i].first!=x&&e[i].second!=x&&(in(u,e[i].first)^in(u,e[i].second))){ok=false;break;}
if(!ok)continue;
int cnt=0,y;
for(int i=0;i<n;i++)if(g[x][i]&&in(u,i))cnt++,y=i;
if(cnt>1)continue;
if(cnt==1)res+=dfs(y,u)*dfs(x,U^u^(1<<x));
else for(y=0;y<n;y++)if(in(u,y))res+=dfs(y,u)*dfs(x,U^u^(1<<x));
}
// printf("%d,%d:%d\n",x,U,res);
return res;
}
signed main(){
scanf("%lld%lld%lld",&n,&m,&p),memset(f,-1,sizeof(f));
for(int i=0,x,y;i<m;i++)scanf("%lld%lld",&x,&y),x--,y--,g[x][y]=g[y][x]=true,e[i]=make_pair(x,y);
for(int i=0,a,b,c;i<p;i++)scanf("%lld%lld%lld",&a,&b,&c),a--,b--,c--,l[i]=make_pair(make_pair(a,b),c);
for(int i=0;i<n;i++)f[i][1<<i]=1;
printf("%lld\n",dfs(0,(1<<n)-1));
return 0;
}