CF599E Sandy and Nuts

XLIV.CF599E Sandy and Nuts

神题。

本题给我一个忠告:无论什么题,都要先看数据范围(废话)。

没看到n13之前以为是道毒瘤题,看到之后……还是毒瘤题。

因为数据范围小,可以状压。

先不考虑LCA和边的限制。设f[x][U]表示:在以x为根的子树中,选择了U里面的点,的方案数。

转移就是枚举u{Ux},其中符号表示从某个集合中删掉一个数/一个集合。这个u表示x的某个儿子所包含的子树集。然后就有

f[x][U]=u{Ux}yuf[y][u]f[x][Uu]

但是这个枚举会重复计算:假设x有两个儿子y1y2,那么枚举y1时,y2的情况会被算上;同时,枚举y2时,y1也会被计算!

这样,我们必须只计算包含某个点pos的那种方案所贡献的答案。即,随便找出一个pos{Ux},则只有uposu才是合法的u

下面我们考虑加上边和LCA的限制,什么样的u才是合法的u

I.LCA的限制

I.I.确保LCA是LCA而不是单纯的CA(common ancestor)。

即,对于(ai,bi,ci),如果有ci=x,则aiubiu不能同时出现,否则它们的LCA就不是x了。

I.II.确保LCA一定是A(ancestor)

即,对于(ai,bi,ci),如果有ciu,必有aiubiu

II.边的限制

II.I.确保边的存在

即,对于(ai,bi),如果有aixbix但是aiubiu却有且只有一个条件成立,则这条边不可能存在。

II.II.确保边的可能

即,对于所有的y s.t. (x,y),一个u里最多只能有这么一个y,因为一棵子树中最多只能同父亲连一条边。

如果只存在一个yu,那么转移就只能从这个y而来,即

f[x][U]=f[y][u]f[x][Uu]

否则,即不存在yu,就是上面的式子

f[x][U]=yuf[y][u]f[x][Uu]

边界为f[x][{x}]=1,最终答案为f[0][{0n1}]

为了方便,采取记忆化搜索的形式实行。

代码:

#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 @   Troverld  阅读(160)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示