http://codeforces.com/contest/599/problem/E
题意不再多说...
我用 dp[i][type][s] 表示以i为根,包含二进制s所表示的节点(不含i)的子树的方案数,type的作用放到后面解释。
那么答案就是 dp[0][0][(1<<n)-2]
状态转移的时候我先分离出当前sta最低位的1(lowbit), 然后我枚举和这一位组成一颗子树的情况(这样使得枚举不会重复,队友说的。。)。
对于情况s,要判断是否有效,我枚举以i为公共祖先的点对,要求不能同时在s里,并且对于每个s中的点,与它相连的点要在s中或者等于i。
对于符合条件的s, 枚举某个点j作为子树的根, dp[i][type][sta] += dp[j][0][s] * dp[i][1][sta -s].
type == 0 表示未被分割的情况,type== 1表示另外还有子树连着根。这是为了当type==0时,我要首先判断一下以i为公共祖先的点对要都在点集里。
递归结束条件是sta == 0 时 return 1。
(感觉并没有讲清楚T^T)
#include
using namespace std;
typedef long long ll;
#define rep(i,s,t) for(int i=int(s); i
pii;
int n,m,q;
int linked[15];
ll dp[15][3][1<<13+5];
pii mlca[15][105];
int cnt_lca[15];
bool judge(int root, int s, int mods)
{
rep(i, 0, n) if((s >> i) & 1)
{
if((linked[i] | mods) != mods) return false;
}
rep(i, 0, cnt_lca[root])
{
if(((s >> mlca[root][i].first) & 1) && ((s >> mlca[root][i].second) & 1)) return false;
}
return true;
}
ll dpit(int root, int beg, int sta) // root 0 ~ n-1
{
if(~dp[root][beg][sta]) return dp[root][beg][sta];
if(beg == 0)
{
int tmps = sta + (1 << root);
rep(i, 0, cnt_lca[root])
{
if(((tmps >> mlca[root][i].first) & 1) && ((tmps >> mlca[root][i].second) & 1)) continue;
return dp[root][beg][sta] = 0;
}
}
if(sta == 0)
{
return 1;
}
ll& ret = dp[root][beg][sta];
ret = 0;
int has = (sta & (-sta));
sta -= has;
for(int s = 0; s<=sta; s++) if((s | sta) == sta)
{
int ss = s + has;
if(!judge(root, ss, ss + (1 << root))) continue;
for(int i=0; i
> i) & 1)
{
ret += dpit(i, 0, ss - (1 << i)) * dpit(root, 1, sta - s);
}
}
//cout<
<<" "<
<<" "<
<< b); linked[b] += (1 << a); } rep(i, 0, q) { scanf("%d%d%d", &a, &b, &c); a --, b --, c --; mlca[c][cnt_lca[c]++] = {a, b}; } mst(dp, -1); ll ans = dpit(0, 0, (1<