CF599E Sandy and Nuts

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;
} 

posted @ 2021-03-30 16:05  Troverld  阅读(95)  评论(0编辑  收藏  举报