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

  • 相关阅读:
    android 网络请求Volley的简单使用
    数据加密,android客户端和服务器端可共用
    非常有用的GitHub链接
    android开发——Android开发中的47个小知识
    EditText禁用系统键盘,光标可以继续使用
    Android Studio 快速开发
    Android系统拍照之后回显并且获取文件路径
    Android为TV端助力 doc里面adb连接出现问题的解决方法
    Android为TV端助力 自定义view中findViewById为空的解决办法
    Android为TV端助力 VelocityTracker 速度追踪器的使用及创建
  • 原文地址:https://www.cnblogs.com/Troverld/p/14597281.html
Copyright © 2020-2023  润新知